算法复杂度

相关概念:

稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。

不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。

时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。

空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数

手写快排:

先选择第一个数字作为标尺,然后分别从第二个数字往右找,找到比第一个数大的数,和从倒数第一个数字往左找,找到比第一个数小的数,然后将找到的两个数进行交换,一直下去。

  • 从数列中挑出一个元素,称为 “基准”(pivot);
  • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
public class quickSort {
	private static int[] quickSort(int[] arr, int left, int right) {
		if (left < right) {
			int p = patition(arr, left, right);//基数
			quickSort(arr, left, p - 1);
			quickSort(arr, p + 1, right);
		}
		return arr;
	}
	private static int patition(int[] arr, int left, int right) {// 分区操作
		int temp = left;
		while (left < right) {
			//从右边开始找到比主键值a[0]小的值,移到左边
			while (left < right && arr[right] >= arr[temp])
				right--;
			while (left < right && arr[left] <= arr[temp])
			//从左边开始找到比主键值a[0]大的值,移到右边
				left++;
			swap(arr,left,right);
		}
		//最终将基准数归位 
		swap(arr, temp, right);
		return right;

	}
	private static void swap(int[] arr, int left, int right) {
		int temp=arr[left];
		arr[left]=arr[right];
		arr[right]=temp;
		
	}
}

手写归并:

该算法采用经典的分治(divide-and-conquer)策略(分治法将问题(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

  • 把长度为n的输入序列分成两个长度为n/2的子序列;
  • 对这两个子序列分别采用归并排序;
  • 将两个排序好的子序列合并成一个最终的排序序列。
public class MergeSort {

	public static void main(String[] args) {
		int[] arr = { 9, 8, 7, 6, 5, 4, 3, 2, 1 };
		int[] temp = new int[arr.length];
		sort(arr, 0, arr.length - 1, temp);
		System.out.println(Arrays.toString(arr));
	}

	private static void sort(int[] arr, int left, int right, int[] temp) {
		if (left < right) {
			int mid = (left + right) / 2;
			sort(arr, left, mid, temp);
			sort(arr, mid + 1, right, temp);
			MergeSort(arr, left, mid, right, temp);
		}

	}

	private static void MergeSort(int[] arr, int left, int mid, int right, int[] temp) {
		int i = left;
		int j = mid + 1;
		int t = 0;
		while (i <= mid && j <= right) {
			if (arr[i] <= arr[j]) {
				temp[t++] = arr[i++];
			} else {
				temp[t++] = arr[j++];
			}
		}
		while (i <= mid) {// 将左边剩余的元素填充进temp
			temp[t++] = arr[i++];
		}
		while (j <= right) {
			temp[t++] = arr[j++];
		}
		t = 0;
		while (left <= right) {
			arr[left++] = temp[t++];
		}
	}

}

手写堆排序:

堆排序

  堆排序是利用这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。

  堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。

堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

用简单的公式来描述一下堆的定义就是:

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  

小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]  

public class HeapSort {
	private static void 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--){
			swap(arr,0,j);//将堆顶元素与末尾元素进行交换
			adjustHeap(arr, 0, j);//重新对堆进行调整
		}
		
	}
	private static void swap(int[] arr, int a, int b) {
		int temp=arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
	}

	private static void adjustHeap(int[] arr, int i, int length) {
		int temp=arr[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++;
			}
			if(arr[k]>temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
				arr[i]=arr[k];
				i=k;
			}else{
				break;
			}
		}
		arr[i]=temp;//将temp值放到最终的位置	
	}
}

参考博文:

十大经典排序算法(动图演示)https://www.cnblogs.com/onepixel/articles/7674659.html

https://www.cnblogs.com/chengxiao/p/6103002.html