数据结构—排序总结

排序根据是否使用外存分为内排序和外排序,内排序只使用内存进行数据存储,外排序由于数据量比较大需要借助外存。

排序的稳定性:排序的稳定性是指排序之后相同的数据元素相对位置不变则为稳定排序,否则为不稳定排序。

插入排序

直接插入排序

思想:将一个记录插入到已经排序的有序表中,从而得到一个新的、个数加1的有序表。这个过程在查找位置过程中进行记录移动,而不用显示的交换元素

时间复杂度:O(n^2)

空间复杂度:O(1)

稳定性:稳定排序

public void insertSort(int[] a){
		int j;
		for(int i= 1;i<a.length;i++){
			int tmp = a[i];
			for( j=i;j>0 && compare(a[j-1],tmp) ; j--)
				a[j] = a[j-1];
			a[j] = tmp;
		}
	}
   
private boolean compare(int a,int b){
        return a>b;
    }

折半插入排序

思想:直接插入排序的过程就是查找、比较,如果比较的次数相对少了其效率 也会有所提高,因为需要在已经排好序的序列查找插入位置,所以使用折半查找降低查找次数

时间复杂度:O(n^2)

空间复杂度:O(1)

稳定性:稳定排序

public void bInsertSort(int[] a){
		int j;
		for(int i=1;i<a.length;i++){
			int tmp = a[i];
			int low = 0;
			int high = i-1;
			while(low < high){
				int mid = (low + high)/2;
				if(tmp < a[mid])
					high = mid -1;
				else
					low = mid + 1;
			}
			for(j=i;j>low;j--)
				a[j] = a[j-1];
			a[j] = tmp;
		}
	}

希尔排序

思想:在插入排序的基础上,由于插入排序如果序列基本有序的话他的效率会很高,如果直接有序的话其时间复杂度是O(n);所以希尔排序将待排序列分割成若干个子序列,分别进行插入排序,待整个序列基本有序再进行一次整体排序。希尔排序的时间复杂度依赖于其增量序列。

时间复杂度:可达到O(n3/2)

空间复杂度:O(1)

稳定性:不稳定(跳跃性插入)

public void shellSort(int[] a){
		int j;
		int length = a.length;
		for(int skp=length/2;skp>0;skp/=2){
			for(int i=skp;i<length;i++){
				int tmp =a[i];
				for(j = i;j>=skp && compare(a[j-skp],tmp);j-=skp)
					a[j] = a[j-skp];
				a[j] = tmp;
			}
		}
	}
	private boolean compare(int a,int b){
		return a>b;
	}

我这里使用的是希尔增量序列,他并不是最好的增量序列,比如当其个数是2的幂次方时候,每次其skp都是偶数位,如果偶数为都是方最大值,基数为方最小值,这时希尔排序并没有其什么作用,增量序列应该保证其相邻增量互质,最小值为1

交换排序

冒泡排序

思想:通过第一记录与第二个记录比较如果若为逆序则交换其位置,然后比较第二个与第三个记录,直到n-1项与第n项比较结束。这个过程是一次冒泡排序,选择出最大值,然后继续对前n-1个值进行比较位置,以此类推。整个排序过程需要n-1次冒泡排序。

时间复杂度:O(n^2)

空间复杂度:O(1)

稳定性:稳定排序

public void bubbleSort(int[] a){
		for(int i=0;i<a.length-1;i++)
			for(int j=0;j<a.length-i-1;j++)
				if(a[j] > a[j+1]){
					int tmp = a[j];
					a[j] = a[j+1];
					a[j+1] = tmp;
				}
	}

快速排序

思想:冒泡排序的改进,一趟排序将待排序序列分为两部分,其中一部分关键词都大于另一部分关键词,然后在对这两部分序列继续进行排序,以达到整个序列有序。其实现需要找到一个枢纽元素将序列与枢纽元素进行对比,小于枢纽元素放在一侧,大于枢纽元素放在另一侧。枢纽元素的选择很重要,一般是选择序列的第一个元素,但是着存在问题,当待排序列是正序或者逆序的时候所有元素都会倾向一侧,这样在整个递归过程中都是这样,使其效率地下。可以选择待排序列的第一元素、中间元素、最后一个元素进行比较然后选择中间值,作为枢纽元素。

时间复杂度:O(nlogn)

空间复杂度:O(1)

稳定性:不稳定排序

	public void quickSort(int[] a,int left ,int right){
		int dp;
		if(left < right){
			dp = partition(a, left, right);
			quickSort(a, left, dp-1);
			quickSort(a, dp+1, right);
		}
	}
	private  int partition(int[] a,int left, int right){
        int pivot = a[left];
        while(left < right){
            while(left < right && a[right] >= pivot)
                right--;
            if(left < right)
                a[left++] = a[right];
            while(left < right && a[left] < pivot)
                left++;
            if(left < right)
                a[right--] = a[left];
        }
        a[left] = pivot;
        return left;
    }

选择排序

简单选择排序

思想:每一次从n-i个记录中选取最小的元素,作为有序序列中的第i个记录

时间复杂度:O(n^2)

空间复杂度:O(1)

稳定性:不稳定排序

public void selectSort1(int[] a){
		for(int i=0;i<a.length;i++){
			int k = i;
			for(int j=i+1;j<a.length-1;j++){
				if(a[j] < a[k])
					k = j;
			}
			if(k != i){
				int tmp = a[i];
				a[i] = a[k];
				a[k] = tmp;
			}
		}
	}

堆排序

思想:堆排序的思想就是将待排序列看做完全二叉树,堆分为大堆和小堆,大根堆的意思就是每棵子树的根节点大于其左右子树的根节点,小根堆的与之相反。由待排序列建堆的过程就是筛选最大值的过程,查找出最大值后将该值存放在待排序数组的最后一个位置length-1,下一个最大值放在length-2位置。由于是完全二叉树,所以左后一个非终端节点是n-1/2(因为数组下标从0开始),用此值开始和左右子树进行比较选取最大值后以此向上推。由于大根堆得出的序列是由小到大所以,如果是需要正序,则构造大根堆,如果需要逆序则构造小根堆。

时间复杂度:O(nlogn)

空间复杂度:O(1)

稳定性:不稳定排序

public void heapSort(int[] a){
		for(int i=0;i<a.length;i++){
			createMaxHeap(a,	a.length-i-1);
			swap(a,i,a.length-i-1);
		}
	}
	private void createMaxHeap(int[] a,int lastIndex){
		for(int i=(lastIndex-1)/2;i>=0;i--){
			int k = i;	//记录当前节点
			while(2*k+1 <= lastIndex){	//判断子节点是否存在
				int bigIndex = 2*k+1;		//记录最大值节点,默认赋值为左孩子节点
				if(bigIndex < lastIndex){	//判断是否存在右孩子
					if(a[bigIndex +1] > a[bigIndex])		//找孩子节点中的最大值
						bigIndex++;
				}
				if(a[k] < a[bigIndex]){	//判断孩子节点的最大值与根节点值进行比较
					swap(a,k,bigIndex);
					k = bigIndex;
				}else
					break;
			}
		}
	}
	private void swap(int[] a,int i,int j){
		int tmp = a[i];
		a[i] = a[j];
		a[j] = tmp;
	}

归并排序

思想:将两个或两个以上的有序表合并成一个新的有序表。假如有n个记录的待排序列,首先将其分为n个子序列,然后两两合并得到n/2个子序列,然后再两两合并,重复执行。直到得到一个长度为n的有序序列,这种排序方法称为2-路归并排序;

时间复杂度:O(nlogn)

空间复杂度:O(n)

稳定性:稳定排序

	public static  void mergeSort(int[] a){
		sort(a,0,a.length-1);
		for (int i : a) {
			System.out.println(i);
		}
	}
	private static void sort(int[] a, int left,int right){
		if(left < right){
			int center = (left + right)/2;
			sort(a,left,center);
			sort(a,center+1,right);
			merge(a,left,center,right);
		}
	}
	private static void merge(int[] a,int left,int center,int right){
		int[] tmpArr = new int[a.length];
		int mid = center+1;
		int count = left;
		int tmp = left;
		while(left <=center && mid <= right){
			if(a[left] <= a[mid])
				tmpArr[count++] = a[left++];
			else
				tmpArr[count++] = a[mid++];
		}
		while(left<=center)
			tmpArr[count++] = a[left++];
		while(mid<=right)
			tmpArr[count++] = a[mid++];
		while(tmp <= right)
			a[tmp] = tmpArr[tmp++];
	}

桶式排序

思想:对于使用桶式排序,创建一个数组,数组大小为其待排序元素的最小值和最大值只差,使用该数组下标存储待排序列,然后使用公式a[i] = a[i] + a[i-1];计算各个记录在序列中的位置。桶式排序需要保证待排序列其范围与最大值最小值差不多如果,其最大值最小值差远远大于元素个数,那么辅助空间花销会很大。

时间复杂度:O(n+m) m为范围

空间复杂度:O(n)

稳定性:稳定排序

	public void bucketSort(int[] a,int max,int min ){
		//缓存数据
		int[] tmp = new int[a.length];
		//桶数组,他的大小应该为所给数组中最大值减去最小值
		int [] buckets = new int[max - min];
		//将每个记录掉入桶中
		for(int i=0;i<a.length;i++)
			buckets[a[i]-min]++;
		//计算桶中元素在有序序列中的位置
		for(int i=0;i<max-min;i++)
			buckets[i] = buckets[i] + buckets[i-1];
		tmp = Arrays.copyOf(a, a.length);
		for(int i=a.length-1;i>=0;i--)
			a[--buckets[tmp[i]-min]] = tmp[i];
	}

基数排序

思想:基数排序是一种借助多关键字排序思想的排序。他将待排记录分解成多个关键字,根据子关键字进行排序 。多关键字排序有两种排序方式,最高位优先法(MSD)和最低位优先法(LSD),他是按照关键字的位数,比如123,分解成三个关键字百位、十位、分位。计算机更合适于LSD最低位优先法,关键字之间的排序还需要借助另一种稳定的排序,如果待排序列是整数,那么每一位都在[0-9]之间,因此可以使用桶式排序。

时间复杂度:依赖于子关键字排序算法

空间复杂度:依赖于字关健排序算法

稳定性:稳定排序

	//radix=10,d为位数
	public void radixSort(int[] a,int radix ,int d){
		//创建缓存数据
		int[] tmp = new int[a.length];
		//buckets记录排序元素的信息
		int[] buckets = new int[radix];
		//总共排序位数最多次
		for(int i=0,rate=1;i<d;i++){
			//重置count数组
			Arrays.fill(a, 0);
			//复制缓冲数据
			tmp = Arrays.copyOf(a, a.length);
			//将每一位放入桶中
			for(int j=0;j<a.length;j++){
				int subKey = (a[j]/rate)%radix;
				buckets[subKey]++;
			}
			//计算桶中元素在序列中的位置
			for(int j=1;j<radix;j++)
				buckets[j] = buckets[j] + buckets[j-1];

			//按子关键字对指定的数据排序
			for(int j=a.length-1;j>=0;j--){
				int subKey = (a[j]/rate)%radix;
				a[--buckets[subKey]] = tmp[j];
			}
			rate *=radix;
		}
	}

总结:

算法 时间复杂度 空间复杂度 稳定性
直接插入排序 O(n^2) O(1) 稳定
折半插入排序 O(n^2) O(1) 稳定
希尔排序 O(n3/2) O(1) 不稳定
冒泡排序 O(n^2) O(1) 稳定
快速排序 O( nlogn) O(1) 不稳定
简单选择排序 O(n^2) O(1) 不稳定
堆排序 O(nlogn) O(1) 不稳定
归并排序 O(nlogn) O(n) 稳定
桶式排序 O(K+n) O(K+n) 稳定

基数排序               依赖于子关键字排序算法       依赖于子关键字排序算法         稳定

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-10 01:43:37

数据结构—排序总结的相关文章

数据结构--排序

插入排序(上)     基本思想:每次将一个待排序的的元素,按其关键字大小插入到已经排好序的子表的适当位置,直到全部元素插完为止.直接插入排序    简写排序思路:     假设待排序的元素存放在R[0.....n-1]中,在排序过程中,将R划分为两个区间,分别为R[0.....i-1]和R[i....n-1](刚开始时i=1,有序区只有R[0]一个元素),    其中,前一个子区间即是一个已经排好序的子区间,即有序区,后一个子区间则是一个待排序的无序区.   直接插入排序的操作即是:将当前无序

黑马程序员——数据结构排序算法总结

-----------android培训.java培训.java学习型技术博客.期待与您交流!------------ 下面是几个网上常见的总结图: 有些不同之处:集中在希尔排序的时间复杂度.快速归并的空间复杂度上 个人总结口诀: 选择N方-->选择排序的最好最坏平均都N方 插入冒泡最好N-->插入冒泡的最好是N,其他是N方 归并堆n乘logN-->归并希尔堆的最好最坏平均都是n乘logN 快速最坏N方-->快速排序的最坏是N方,其他是n乘logN 快速选择希尔堆不稳定-->

数据结构排序算法Java实现

闲的无聊又拿起了大学的数据结构的书看起来 <数据结构(c语言版)>严蔚敏 吴伟民编著. 里面有几个排序算法,感觉挺好玩的,就想着写出来玩玩. package test.sort; /** * @Title: TODO * @Description: TODO * @author: * @date: 2014-8-10 上午11:20:43 * */ public class quickSort { private static int datas[] = {23,42,12,45,56,63,

20175314薛勐 数据结构-排序(选做)

数据结构-排序(选做) 要求 在数据结构和算法中,排序是很重要的操作,要让一个类可以进行排序,有两种方法: 有类的源代码,针对某一成员变量排序,让类实现Comparable接口,调用Collection.sort(List) 没有类的源代码,或者多种排序,新建一个类,实现Comparator接口 调用Collection.sort(List, Compatator) 针对下面的Student类,使用Comparator编程完成以下功能: 在测试类StudentTest中新建学生列表,包括自己和学

数据结构-排序(选做) 20175204

数据结构-排序(选做) 任务要求 在数据结构和算法中,排序是很重要的操作,要让一个类可以进行排序,有两种方法: 有类的源代码,针对某一成员变量排序,让类实现Comparable接口,调用Collection.sort(List) 没有类的源代码,或者多种排序,新建一个类,实现Comparator接口 调用Collection.sort(List, Compatator) 针对下面的Student类,使用Comparator编程完成以下功能: 在测试类StudentTest中新建学生列表,包括自己

数据结构——排序算法总结

排序(Sorting)就是将一组对象依照规定的次序又一次排列的过程,排序往往是为检索而服务的.它是数据处理中一种非常重要也非经常常使用的运算.比如我们日常学习中的查字典或者书籍的文件夹.这些都事先为我们排好序,因此大大减少了我们的检索时间,提高工作效率. 排序可分为两大类: 内部排序(Internal Sorting):待排序的记录所有存放在计算机内存中进行的排序过程: 外部排序(External Sorting):待排序的记录数量非常大,内存不能存储所有记录.须要对外存进行訪问的排序过程. 外

Python中对复杂数据结构排序(类似C中结构体数据结构)

Python中排序主要有两个函数:sorted和列表成员函数sort,两者除了调用方式有些区别外,最显著的区别是sorted会新建一个排序好的列表并返回,而sort是修改原列表并排好序.sorted的原型是: sorted(iterable, cmp=None, key=None, reverse=False) sort的原型是: list.sort(cmp=None, key=None, reverse=False) 其中cmp和key都是函数引用,即可以传入函数名.这两个函数都是对list里

数据结构-排序算法总结

排序算法 算法分析 算法稳定性 如果一种排序算法不会改变关键码值相同的记录的相对顺序,则称为稳定的(stable) 不稳定的算法在某种条件下可以变为稳定的算法,而稳定的算法在某种条件下也可以变为不稳定的算法.例如,对于冒泡排序算法,原本是稳定的排序算法,如果将记录交换的条件改成a[j].key>=a[j+1].key,则两个相等的记录就会交换位置.再如,快速排序原本是不稳定的排序方法,但若待排序记录中只有一组具有相同关键码的记录,而选择的轴值恰好是这组相同关键码中的一个,此时的快速排序就是稳定的

20175324数据结构-排序

针对下面的Student类,使用Comparator编程完成以下功能: 在测试类StudentTest中新建学生列表,包括自己和学号前后各两名学生,共5名学生,给出运行结果(排序前,排序后) 对这5名同学分别用学号和总成绩进行增序排序,提交两个Comparator的代码 课下提交代码到码云 import java.util.Comparator; public class IDComparator implements Comparator { @Override public int comp

数据结构排序-堆排序

堆排序就是利用了最大堆(或最小堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字变得简单.以最大堆为例,它的基本思想就是: 先将初始文件R[1..n]建成一个最大堆,此堆为初始的无序区: 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key: 由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆.然后再次