最全排序算法整合回顾


最近打算重新过一遍算法,所以在此汇总一下常用的算法。
这次会更加注重算法的原理,剔除杂七杂八的东西,用最简单的代码体现算法的原理,希望大家可以和我一样有所收获。

冒泡排序

冒泡排序是我们接触到的第一个排序算法,原理如下:

  1. 比较相邻的元素,如果第一个比第二个大,就交换位置。
  2. 从开始一对到末尾一对做相邻的动作,做完后最后一个位置应是最大数
  3. 每次比较忽略上一轮的最大数,重复N-1次完成排序

所以时间复杂度为O(N2),额外空间复杂度O(1)
冒泡可实现稳定性(排序后相同元素相对次序不变)
代码如下:
java语言,swap交换算法自己写下即可

public static void bubbleSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int e = arr.length - 1; e > 0; e--) {
			for (int i = 0; i < e; i++) {
				if (arr[i] > arr[i + 1]) {
					swap(arr, i, i + 1);
				}
			}
		}
	}

插入排序

例如排序一个长度为N的数组

  1. 把数组看成两个部分,第1个有序,2-N的元素无序
  2. 依次把无序中的元素往有序里插入,插入一次后变成,第1-2的元素有序,3-N的元素无序。
  3. 重复到无序中没有元素即完成排序。

所以最坏时间复杂度为O(N2),最好时间复杂度为O(N)
额外空间复杂度为O(1)
插入排序也可以实现成稳定性

代码如下:

public static void insertionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int i = 1; i < arr.length; i++) {
			for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
				swap(arr, j, j + 1);
			}
		}
	}

注意往有序部分插入时是从后往前比较,你们细品。

选择排序

类似于冒泡,冒泡时相邻元素比较交换,这里是找出无序部分的最值。

  1. 在第1-N个元素中找到最小的那个与第一个元素(0号位)交换
  2. 在2-N找最小的元素与第二个元素(1号位)交换
  3. 一共重复N-1次

因此时间复杂度为O(N2) 额外空间复杂度为O(1)
是不稳定算法。(同值元素的相对次序会被破坏)
例如:2 2 2 1 3
代码如下:

public static void selectionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int i = 0; i < arr.length - 1; i++) {
			int minIndex = i;
			for (int j = i + 1; j < arr.length; j++) {
				minIndex = arr[j] < arr[minIndex] ? j : minIndex;
			}
			swap(arr, i, minIndex);
		}
	}

归并排序

归并,就是讲两个有序数组合并成一个数组,过程可以形容为:

  1. A,B两个数组各设置两个标记位m,n,起始都指向0位置
  2. 两个标记为比较,如果A[m]<=B[n],则A[m]存入新数组,m++否则B[n]存入新数组,n++
  3. A,B数组中的一个比较完毕,则另外一个数组剩下元素直接按序存入新数组。

因为是两个有序数组所以可以这么做。而归并排序就是利用这点来递归,一个无序数组,不断二分二分,最后一定会有两个数组都只有一个元素,一个元素的数组可视为有序,再一步步追溯来排序。

归并排序的时间复杂度O(N*logN) 额外空间复杂度O(N)
归并排序可以实现稳定性

	public static void mergeSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		sortProcess(arr, 0, arr.length - 1);
	}

	public static void sortProcess(int[] arr, int l, int r) {
		if (l == r) {
			return;
		}
		int mid = l + ((r - l) >> 1);
		sortProcess(arr, l, mid);
		sortProcess(arr, mid + 1, r);
		merge(arr, l, mid, r);
	}

	public static void merge(int[] arr, int l, int m, int r) {
		int[] help = new int[r - l + 1];
		int i = 0;
		int p1 = l;
		int p2 = m + 1;
		while (p1 <= m && p2 <= r) {
			help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
		}
		while (p1 <= m) {
			help[i++] = arr[p1++];
		}
		while (p2 <= r) {
			help[i++] = arr[p2++];
		}
		for (i = 0; i < help.length; i++) {
			arr[l + i] = help[i];
		}
	}

快速排序

基于分治的思想,是冒泡排序的改进型。首先在数组中选择一个基准点(该基准点的选取可能影响快速排序的效率,后面讲解选取的方法),然后分别从数组的两端扫描数组,设两个指示标志(low指向起始位置,high指向末尾),首先从后半部分开始,如果发现有元素比该基准点的值小,就交换low和high位置的值,然后从前半部分开始扫秒,发现有元素大于基准点的值,就交换low和high位置的值,如此往复循环,直到low>=high,然后把基准点的值放到high这个位置。一次排序就完成了。以后采用递归的方式分别对前半部分和后半部分排序,当前半部分和后半部分均有序时该数组就自然有序了。

因为时间问题,写好久啦,快排原理概括就引用:包子的百草园的概括了,只附上我写的代码。

方法一单指针

	public static void quickSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		quickSort(arr, 0, arr.length - 1);
	}

	public static void quickSort(int[] arr, int l, int r) {
		if (l < r) {
			swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
			int p = split(arr, l, r);
			quickSort(arr, l, p-1);
			quickSort(arr, p+1, r);
		}
	}
	public static int split(int[] arr,int l,int r) {
		int index=l;//期望位置
		int x=arr[l];
		for(int j=l+1;j<=r;j++) {
			if(arr[j]<x) {
				index++;
				if(index!=j) {
					swap(arr, index, j);
				}
			}
		}
		swap(arr, l, index);
		return index;
	}

方法二:双指针

	public static void quickSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		quickSort(arr, 0, arr.length - 1);
	}

	public static void quickSort(int[] arr, int l, int r) {
		if (l < r) {
			swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
			int p = partition(arr, l, r);
			quickSort(arr, l, p-1);
			quickSort(arr, p+1, r);
		}
	}
	public static int partition(int a[],int l,int r) {
		int x=a[l];
		int i=l;
		int j=r;
		while(i<j) {
			while(i<j && a[j]>=x)
				j--;
			while(i<j && a[i]<=x)
				i++;
			//if(i<j)
			swap(a, i, j);
		}
		swap(a, i, l);
		return i;
	}

大神炫技法:不提倡就少用个参数,但是划分更清晰
这里是每次排序分成三个部分 小于基准值,等于基准值,大于基准值。

public static void quickSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		quickSort(arr, 0, arr.length - 1);
	}
	
public static void quickSort(int[] arr, int l, int r) {
		if (l < r) {
			swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
			int[] p = partition(arr, l, r);
			quickSort(arr, l, p[0] - 1);
			quickSort(arr, p[1] + 1, r);
		}
	}
	
	public static int[] partition(int[] arr, int l, int r) {
		int less = l - 1;
		int more = r;
		while (l < more) {
			if (arr[l] < arr[r]) {
				swap(arr, ++less, l++);
			} else if (arr[l] > arr[r]) {
				swap(arr, --more, l);
			} else {
				l++;
			}
		}
		swap(arr, more, r);
		return new int[] { less + 1, more };
	}

堆排序

swap交换函数自己补下噢,原理以后填坑,先贴代码了。

public static void heapSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			heapInsert(arr, i);
		}
		int size = arr.length;
		swap(arr, 0, --size);
		while (size > 0) {
			heapify(arr, 0, size);
			swap(arr, 0, --size);
		}
	}

	public static void heapInsert(int[] arr, int index) {
		while (arr[index] > arr[(index - 1) / 2]) {
			swap(arr, index, (index - 1) / 2);
			index = (index - 1) / 2;
		}
	}

	public static void heapify(int[] arr, int index, int size) {
		int left = index * 2 + 1;
		while (left < size) {
			int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
			largest = arr[largest] > arr[index] ? largest : index;
			if (largest == index) {
				break;
			}
			swap(arr, largest, index);
			index = largest;
			left = index * 2 + 1;
		}
	}

非比较排序

桶排序。计数排序。有兴趣可以自行了解。

额外部分 工程上使用的综合排序

快排,堆排都不具有稳定性,
不追求稳定性一般用快排,
追求稳定性使用归并排序,
比较排序,还需要学会使用比较器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值