常见的排序算法代码详解------稳定性与复杂度分析

1.常见排序算法:

  • 冒泡排序(稳定)

定义:每一趟排序浮出一个小(大)的来放到正确的位置,也就是每一趟找一下没排里边最小(大)的直到排完。

最好 最坏 平均 空间复杂度
o(n) o(n^2) o(n^2) o(1)

代码:

  private static void bubble_sort(int[] a) {//排序核心方法
		for (int i = 0; i < a.length; i++) {
			for (int j = a.length - 1 - i; j > 0; j--) {//每一趟找出一个最大的排 好
				if (a[j] <a[j - 1]) {
					int tmp = a[j];
					a[j] = a[j - 1];
					a[j - 1] = tmp;
				}
			}
		}
		for (int i = 0; i < a.length; i++)//将排好序的输出
			System.out.print(a[i] + " ");
	}
}
  • 快速排序(不稳定)

定义:是对冒泡排序的改进,就是有一个基准,在排完一趟后,基准左边的数都比他的值小,基准右边的值都比他大 。

从右向左找到比基准小的值,将right指向 将该值赋给左边left,从左向右找到比基准大的,放到右边right;一直重复直到right与left重合。------这样一趟就拍完了。

最好 最坏 平均 空间复杂度

O(N*log2N)

O(N2)

O(N*log2N) 

O(log2n)~O(n) 

代码:

  private static void quirt_sort(int[] a, int i, int j) {
		if(i<j){
			int left=i;
			int right=j;
			int tmp=a[left];
			while (left < right && tmp < a[right])
				right--;
			if (right > left)
				a[left] = a[right];
			while (left < right && a[left] < tmp)
				left++;
			if (right > left)
				a[right] = a[left];
			a[left] = tmp;
			int pivot = left;
			quirt_sort(a, i, pivot - 1);
			quirt_sort(a, pivot + 1, j);
		}
  •  
  • 直接插入排序:稳定

定义:每次插入都是往已经排好序的里头插,相当于第i次插入时前i-1次已经是有序的,当数组是逆序的时候效率是极低的因为每插一个就和前面所有的调换。

最好 最坏 平均 空间复杂度
o(n) o(n^2) o(n^2) o(1)

代码:

  private static void direct_sort(int[] a) {
		for (int i = 1; i < a.length; i++) {// 将n个数字插入,把后面n-1个数插入已排好序的a中
			int tmp = a[i];
			int p;
			for (p = i; p > 0; p--) {
				if (a[p - 1] > tmp)
					a[p] = a[p - 1];
				else
					break;
			}
			if (p != i)
				a[p] = tmp;

		}
  •  
  • 选择排序:不稳定

定义:每一趟排序排出一个最小(大)的,进行交换的次数很少

最好 最坏 平均 空间复杂度
o(n^2) o(n^2) o(n^2) o(1)

代码:

  private static void select_sort(int[] a) {
		for(int i=0;i<a.length;i++){
			int p=i;
			for(int j=i;j<a.length;j++) if(a[p]>a[j]) p=j;//找出这趟最小的
			if(p!=i){//如果最小的不是原来的位置,就交换
				int tmp=a[p];
				a[p]=a[i];
				a[i]=tmp;
			}
		
	}
  •  
  • 堆排序:不稳定(是一种选择排序)

定义:是一种树形选择排序,利用二叉树的性质。构造一个大(小)根堆,完了根与最后一个交换,把剩下的在重新构造大根堆,再交换......重复直到排好序。

顺序排序用大根堆,逆序排序用小根堆。

最好 最坏 平均 空间复杂度
o(nlogn) o(nlogn) o(nlogn) o(1)

代码:

  public static void dui_sort(int[] arr) {
		// 1.构建大顶堆
		for (int i = arr.length / 2 - 1; i >= 0; i--) {
			// 从倒数第一个非叶子结点,从右至左调整结构
			adjustHeap(arr, i, arr.length);
		}
		// 2.调整堆结构
		for (int j = arr.length - 1; j > 0; j--) {
			// 将堆顶元素与末尾元素进行交换
			int tmp = arr[0];
			arr[0] = arr[j];
			arr[j] = tmp;
			adjustHeap(arr, 0, j);// 重新对堆进行调整
		}

	}

	public static void adjustHeap(int[] arr, int i, int length) {
		int temp = arr[i];//先取出当前元素i---根节点
		for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {// 从i结点的左子结点开始,也就是2i+1处开始
			if (k + 1 < length && arr[k] < arr[k + 1]) {// 如果左子结点小于右子结点,k指向右子结点
				k++;
			}
			if (arr[k] > temp) {// 如果子节点大于父节点,将子节点值赋给父节点 
				arr[i] = arr[k];
				i = k;
			} else {
				break;  
			}
		}
		arr[i] = temp;// 将当前这个节点--根节点与其子节点对比后,放到正确的位置上
	}

}
  •  
  • 归并排序:稳定

定义:设两个有序的子文件(相当于输入堆)放在同一向量中相邻的位置上:R[low..m],R[m+1..high],先将它们合并到一个局部的暂存向量R1(相当于输出堆)中,待合并完成后将R1复制回R[low..high]中。

具体就是两两合并........;每一趟将part的相邻部分调用merge方法排序合并

最好 最坏 平均 空间复杂度
o(nlogn) o(nlogn) o(nlogn) o(n)

代码:

import java.util.Scanner;

class C {

	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		int n = scan.nextInt();
		int to_sort[] = new int[n];
		for (int i = 0; i < n; i++) {
			to_sort[i] = scan.nextInt();
		}
		for (int gap = 1; gap < to_sort.length; gap++) {
			co_sort(to_sort, gap, to_sort.length);
		}
		for (int i = 0; i < n; i++) {
			System.out.print(to_sort[i] + " ");
		}

	}

	private static void co_sort(int[] a, int gap, int length) {
		int i;
		for (i = 0; i + gap * 2 - 1 < length; i = i + 2 * gap) {// 就是将gap中的调用方法排序,之后再排下一个gap中的
			merg(a, i, i + gap - 1, i + 2 * gap - 1);// 因为两个里边分别是有序的,这样通过与中间那个比较,可以减少比较次数

		}

	}

	private static void merg(int[] a, int low, int mid, int high) {
		int i = low;
		int j = mid + 1;
		int tmp[] = new int[high - low + 1];
		int k = 0;
		while (i <= mid && j <= high) {// 通过比较,将数字有序排到tmp中
			if (a[i] < a[j])
				tmp[k++] = a[i++];
			else
				tmp[k++] = a[j++];

		}
		while (i <= mid) {// 如果左边还剩下,把他们按顺序放入tmp
			tmp[k++] = a[i++];
		}
		while (j <= high) {// 如果右边还剩下,把他们按顺序放入tmp
			tmp[k++] = a[j++];
		}
		for (k = 0, i = low; i <= high; k++, i++) {// 把排好序的放在数组中
			a[i] = tmp[k];
		}

	}

}
  •  
  • 基数排序:稳定

定义:

(1)根据数据项个位上的值,把所有的数据项分为10组;

(2)然后对这10组数据重新排列:把所有以0结尾的数据排在最前面,然后是结尾是1的数据项,照此顺序直到以9结尾的数据,这个步骤称为第一趟子排序;

(3)在第二趟子排序中,再次把所有的数据项分为10组,但是这一次是根据数据项十位上的值来分组的。这次分组不能改变先前的排序顺序。也就是说,第二趟排序之后,从每一组数据项的内部来看,数据项的顺序保持不变;

(4)然后再把10组数据项重新合并,排在最前面的是十位上为0的数据项,然后是10位为1的数据项,如此排序直到十位上为9的数据项。

(5)对剩余位重复这个过程,如果某些数据项的位数少于其他数据项,那么认为它们的高位为0。

最好 最坏 平均 空间复杂度
o(d(n+r)) o(d(n+r)) o(d(n+r)) o(rd+n)

代码:

	private static void jishu_sort(int[] a) {
		// 获取最大值,从而可以的到他的位数
		int max_bit = getMax(a);
		for (int i = 1; max_bit / i > 0; i *= 10) {// 从个位开始排,直到最大位数都排好

			int b[] = new int[10];
			for (int j = 0; j < a.length; j++) {// b[i]用来保存该位为a的个数
				b[a[j] / i % 10]++;
			}
			for (int j = 1; j < 10; j++) {// 从而确定该位为a是第几个结束排
				b[j] += b[j - 1];
			}
			// 临时数组c[i]用来放这些数字排好序的
			int c[] = new int[a.length];
			for (int j = 0; j < a.length; j++) {
				c[b[a[j] / i % 10] - 1] = a[j];// a[j]排的位置:比如该位为m的为5,意思是第五个结束-第五个下标为4
				b[a[j] / i % 10]--;// -------下一个该位为5的就成第四个了
			}
			for (int j = 0; j < a.length; j++) {
				a[j] = c[j];
			}
		}
	}

	private static int getMax(int[] a) {
		int max = a[0];
		for (int i = 1; i < a.length; i++) {
			if (a[i] > max)
				max = a[i];
		}
		return max;
	}

}
  • 二叉树排序:不一定

定义:每一趟排序浮出一个小的来放到正确的位置,也就是每一趟找一下没排里边最小的直到排完。

最好 最坏 平均 空间复杂度
o(nlogn) o(n^2) o(nlogn) o(1)

代码: https://blog.csdn.net/weixin_42565135/article/details/102944281

  • 希尔排序:不稳定

定义:先 取一个小于n的证书d1作为第一个增量,把文件的全部记录分成d1组。所有距离为d1的倍数的记录放在同一组中。先在各组内进行直接插入排序,然后取第二 个增量d2<d1重复上述的分组和排序,直到所取的增量dt=1,即所有记录放在同一组中进行直接插入排序为止。

最好 最坏 平均 空间复杂度
o(n) o(n^2) o(nlogn) o(1)

代码:

  private static void shell_sort(int[] a, int n) {
		for (int gap = n / 2; gap != 0; gap /= 2) {// 进行的趟数,当gap为0是说明已经排好了
			for (int i = 0; i < gap; i++) {// 每趟给gap组来排序
				for (int j = i + gap; j < a.length; j += gap) {// 对每一组进行排序
					if (a[j] < a[j - gap]) {
						int tmp = a[j];// 将该数保存住
						int k = j - gap;
						while (k >= 0 && a[k] > tmp) {
							a[k + gap] = a[k];
							k -= gap;
						}
						a[k + gap] = tmp;
					}

				}

			}
		}

	}

2.稳定排序:在排序之前与排序之后相等的数字仍然保持原来的相对顺序。

3.时间复杂度:算法中语句执行的次数,也就是对排序数据的总操作次数。

   空间复杂度:该算法在执行时所需的内存空间

发布了233 篇原创文章 · 获赞 20 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_42565135/article/details/103042266