七大主流排序

一、插入排序

算法思想:
从第二个数开始,往前找,如果前面的数比自己大,就交换,最终找到合适的位置就结束了。(这个交换可以理解为向后移位,你玩扑克牌的时候,按大小排序,前面插一张,后面的就要往后挪一点)
时间复杂度:
最好:O(n)
最坏:O(n^2)
平均:O(n)
空间复杂度: O(1)
稳定性:稳定

public static void InserSort(int[] arr){
int tmp;
for(int i=0;i<arr.length-1;i++){
	for(int j=i+1;j>0;j--){
		if(arr[j] < arr[j-1]){
		 tmp = arr[j];
		 arr[j] = arr[j-1];
		 arr[j-1] = tmp;
		} else{
		break;
		}
	}
}

}

换个版本

public static void InsertSort(int[] arr){
	for(int i=1;i<arr.length;i++){
		int key = arr[i];
		int j = i-1;
		for(;j>=0 && arr[j] > v;j--){
			arr[j+1] = arr[j];
		}
		arr[j+1] = key;
	}
}

二、希尔排序

算法思想:基于直接插入方法的升级版本,目的就是为了先让数组变得更有序,有序了插入排序的时间复杂度就会降低。

时间复杂度:
最好:O(n)
最坏:O(n^2)
平均:O(n^1.3)
空间复杂度: O(1)
稳定性: 不稳定

public static void ShellSort(int[] arr,int length){
	int pos = length;
	int tmp;
	while(true){
	pos = pos/2;	
	for(int k=0;k<pos;k++){
		for(int i= k+pos;i<arr.length;i+=pos){
			for(int j=i;j>k;j-=pos){
				if(arr[j] < arr[j-pos]){
					tmp = arr[j];
					arr[j] = arr[j-pos];
					arr[j-pos] = tmp; 		
					} else {
					  break;
					}
				}
			}
	    }
	    if(pos == 1){
		   break;
		}
	}
}

换个版本:

public static void shellSort(int[] array) {
	int gap = array.length;
	while (gap > 1) {
	insertSortGap(array, gap);
	gap = (gap / 3) + 1; // OR gap = gap / 2;
	}
	insertSortGap(array, 1);//就是直接插入排序了
}
private static void insertSortGap(int[] array, int gap) {
 	for (int i = 1; i < array.length; i++) {
	int v = array[i];
	int j = i - gap;
	for (; j >= 0 && array[j] > v; j -= gap) {
	array[j + gap] = array[j];
	}
	array[j + gap] = v;
  }
}

三、冒泡排序

算法思想:每次让最大的冒出来;这么说吧,从最后一个往前找,发现左边的比后面的大,就交换,然后第一趟,把最小的放到了前面,第二趟,把次小的放到了前面。

时间复杂度:
最好:O(n)
最坏:O(n^2)
平均:O(n^2)
空间复杂度: O(1)
稳定性: 稳定

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

升级版:主要是为了解决没有交换,说明已经有序,直接跳出。

public static void BubbleSort(int[] arr){
	int tmp;
	boolean flag = false;
	for(int i=0;i<arr.length;i++){
		flag = false;
		for(int j= arr.length-1;j>i;j--){
			if(arr[j] < arr[j-1]){
			tmp = arr[j];
			arr[j] = arr[j-1];
			arr[j-1] = tmp;
			flag = true;
			}
		}
		if(!flag){
		break;	
		}
	}
}

四、选择排序

算法思想:打擂台方法,若为升序,第一个肯定最小,然后我找后面有比它还小的吗,如果有更改最小标志位,直到找到最小的,然后交换。

时间复杂度: O(n^2)
空间复杂度: O(1)
稳定性: 不稳定

 public static void SelectSort(int arr[]){
	int tmp=0;
	for(int i=0;i<arr.length-1;i--){
		int minIndex = i;
		for(int j=i+1;j<arr.length;j++){
			if(arr[minIndex] > arr[j]){
				minIndex = j;
			}
		}
		if(minIndex != i){
		tmp = arr[i];
		arr[i] = arr[minIndex];
		arr[minIndex] = tmp; 
		}
	}
}

五、快速排序

算法思想:先找出一个基准值,找到之后,比他小的放左边,比他大的放右边,排好序后,把临界点的值给返回来。(看代码说话),分治法的思想。
时间复杂度:
最好:O(nlog(n))
最坏:O(n^2)
平均:O(n
log(n))
空间复杂度: 最好平均都是O(log(n)),最坏O(n)
稳定性: 不稳定

递归版本

public static void QuickSort(int[] arr){
	QuickSortHelper(arr,0,arr.length-1);
}

private static void QuickSortHelper(int arr[],int left,int right){
	if(left >= rigth){		//递归结束标志
	return;
	}
	int partIndex = partition(arr,left,right);//找出临界值
	QuickSortHelper(arr,left,partIndex-1);//排序左半部分
	QuickSortHelper(arr,partIndex+1,right);//排序右半部分
}

private static void partition(int[] arr,int left,int right){
int BaseIndex = right;  //以最右边为基准点
int BaseValue = arr[right];
	while(left < BaseIndex){
		while(left < BaseIndex && arr[left] <= BaseValue ){
			left++;		//从左往右找到第一个大于基准值的
		}
		while(left < BaseIndex && BaseIndex <= arr[BaseIndex]){
			BaseIndex--;	//从右边往左找第一个小于基准值的
		}
		swap(arr,left,BaseIndex);//把两个交换
}
	swap(arr,left,right);//把临界点的那个值放到最右边去
	return left;//返回临界值的座标
]
private static void swap(int[] arr,int left,int right){
	int tmp = arr[left];
	arr[left] = arr[right];
	arr[rigth] = tmp;
}

非递归版本:

public static void quickSort(int[] array) {
Stack<Integer> stack = new Stack<>();
stack.push(array.length - 1);
stack.push(0);
while (!stack.isEmpty()) {
int left = stack.pop();
int right = stack.pop();
if (left >= right) {
continue;
}
int pivotIndex = partition(array, left, right);
stack.push(right);
stack.push(pivotIndex + 1);
stack.push(pivotIndex - 1);
stack.push(left);
}
}

private static void partition(int[] arr,int left,int right){
int BaseIndex = right;
int BaseValue = arr[right];
	while(left < BaseIndex){
		while(left < BaseIndex && arr[left] <= BaseValue ){
			left++;
		}
		while(left < BaseIndex && BaseIndex <= arr[BaseIndex]){
			BaseIndex--;
		}
		swap(arr,left,BaseIndex);
}
	swap(arr,left,right);
	return left;
]
private static void swap(int[] arr,int left,int right){
	int tmp = arr[left];
	arr[left] = arr[right];
	arr[rigth] = tmp;
}

六、归并排序

算法思想:是分治法的一个非常典型的应用,将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

时间复杂度: O(n*log(n))
空间复杂度: O(n)
稳定性: 稳定

public static void MergeSort(int arr[]){
	MergeSortHelper(arr,0,arr.length);
}

private static void MergeSort(int[] arr,int left,int right){
	if(left >= right || rigth-left == 1){
		return;
	}
	int mid = (left+right)/2;
	MergeSort(arr,left,mid);//排序左边
	MergeSort(arr,mid,right);//排序右边
	merge(arr,left,mid,right);//合并
}
private static void merge(int[] arr, int left,int mid ,int right){
int length = right-left;
int[] Output = new int[length];//搞个临时数组,就排序的
int OutputIndex = 0;
int i=left;
int j=mid;
while (i < mid && j < right){
	if(arr[i] <= arr[j]){
	Output[OutputIndex++] = arr[i++];插入排序
	} else {
	Output[OutputIndex++] = arr[j++];
	}
}
	while(i < mid){
	Output[OutputIndex++] = arr[i++];//看看哪个子串没空,补上
	}
	while(j < right){
	Output[OutputIndex++] = arr[j++];
	}

	for(int k=0;k<OutputIndex;k++){
	arr[left+k] = Output[k]; //赋值回原数组
	}
}



七、堆排序

算法思想:基本原理也是选择排序,只是不在使用遍历的方式查找无序区间的最大的数,而是通过堆来选择无序区间的最大的数。
堆:其实有一个类似于完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

注意: 排升序要建大堆;排降序要建小堆

时间复杂度: O(n*log(n))
空间复杂度: O(1)
稳定性: 不稳定

public class HeapSort {


    /**
     * 选择排序-堆排序
     * @param array 待排序数组
     * @return 已排序数组
     */
    public static int[] heapSort(int[] array) {
        //这里元素的索引是从0开始的,所以最后一个非叶子结点array.length/2 - 1
        for (int i = array.length / 2 - 1; i >= 0; i--) {
            adjustHeap(array, i, array.length);  //调整堆
        }

        // 上述逻辑,建堆结束
        // 下面,开始排序逻辑
        for (int j = array.length - 1; j > 0; j--) {
            // 元素交换,作用是去掉大顶堆
            // 把大顶堆的根元素,放到数组的最后;换句话说,就是每一次的堆调整之后,都会有一个元素到达自己的最终位置
            swap(array, 0, j);
            // 元素交换之后,毫无疑问,最后一个元素无需再考虑排序问题了。
            // 接下来我们需要排序的,就是已经去掉了部分元素的堆了,这也是为什么此方法放在循环里的原因
            // 而这里,实质上是自上而下,自左向右进行调整的
            adjustHeap(array, 0, j);
        }
        return array;
    }

    /**
     * 整个堆排序最关键的地方
     * @param array 待组堆
     * @param i 起始结点
     * @param length 堆的长度
     */
    public static void adjustHeap(int[] array, int i, int length) {
        // 先把当前元素取出来,因为当前元素可能要一直移动
        int temp = array[i];
        for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {  //2*i+1为左子树i的左子树(因为i是从0开始的),2*k+1为k的左子树
            // 让k先指向子节点中最大的节点
            if (k + 1 < length && array[k] < array[k + 1]) {  //如果有右子树,并且右子树大于左子树
                k++;
            }
            //如果发现结点(左右子结点)大于根结点,则进行值的交换
            if (array[k] > temp) {
                swap(array, i, k);
                // 如果子节点更换了,那么,以子节点为根的子树会受到影响,所以,循环对子节点所在的树继续进行判断
                i  =  k;
            } else {  //不用交换,直接终止循环
                break;
            }
        }
    }

    /**
     * 交换元素
     * @param arr
     * @param a 元素的下标
     * @param b 元素的下标
     */
    public static void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}

【理解多少,全凭个人造化。】

补充

其他非主流排序,就说说算法吧,当做兴趣看一看。
1.基数排序
就是建立一个长数组,数字是多少就放在对应下标,然后里面的数字就是元素的个数,然后遍历一遍数组就得到了排序的结果。优劣性,想想就知道了,数字特别大岂不是凉凉,算是一种空间换时间的做法。(PS:数字大也是可以解决的,选择一个基数,除以这个数来放,有的哈希的味道)

2.睡眠排序
这个算好玩吧,这个是多线程方面的,就是进来一个数字,我就创建一个线程,然后让线程休眠,休眠的时间就是这个数字,到了这个时间,线程显示出这个数字就销毁。这排序可以自己写着玩玩,缺点一样的,数字大休眠到天荒地老。这好像算时间换空间把

3.猴子排序
这纯粹是搞笑来的,据说只要猴子够多,让猴子打字,猴子就能写出一本《哈姆雷特》,而猴子排序也是这样的。就是随机,把数组所有的数字随机排序一下,检查是否有序,没有的话就再来一次。我前面写过C语言版本的

发布了43 篇原创文章 · 获赞 12 · 访问量 3389
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章