十大排序算法

 

 1 冒泡排序 比较相邻元素,如果第一个比第二个大,则交换

class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        // bubbleSort
        int n = nums.size();
        for (int i = 0; i < n - 1; ++i) {
            bool flag = false;
            for (int j = 0; j < n - 1 - i; ++j) {
                if (nums[j] > nums[j + 1]) {
                    swap(nums[j], nums[j + 1]);
                    flag = true;//一旦进入循环,说明需要排序
                }                 
            }
            if (flag == false) break; //无交换,代表当前序列已经最优 
        }
        return nums;
    }
};

2、选择排序(了解)

思路:每一轮选取未排定的部分中最小的部分交换到未排定部分的最开头,经过若干个步骤,就能排定整个数组。即:先选出最小的,再选出第 2 小的,以此类推

import java.util.Arrays;

public class Solution {

    // 选择排序:每一轮选择最小元素交换到未排定部分的开头

    public int[] sortArray(int[] nums) {
        int len = nums.length;
        // 循环不变量:[0, i) 有序,且该区间里所有元素就是最终排定的样子
        for (int i = 0; i < len - 1; i++) {
            // 选择区间 [i, len - 1] 里最小的元素的索引,交换到下标 i
            int minIndex = i;
            for (int j = i + 1; j < len; j++) {
                if (nums[j] < nums[minIndex]) {
                    minIndex = j;
                }
            }
            swap(nums, i, minIndex);
        }
        return nums;
    }

    private void swap(int[] nums, int index1, int index2) {
        int temp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = temp;
    }

    public static void main(String[] args) {
        int[] nums = {5, 2, 3, 1};
        Solution solution = new Solution();
        int[] res = solution.sortArray(nums);
        System.out.println(Arrays.toString(res));
    }
}

总结:

算法思想 1:贪心算法:每一次决策只看当前,当前最优,则全局最优。注意:这种思想不是任何时候都适用。

算法思想 2:减治思想:外层循环每一次都能排定一个元素,问题的规模逐渐减少,直到全部解决,即「大而化小,小而化了」。运用「减治思想」很典型的算法就是大名鼎鼎的「二分查找」。

优点:交换次数最少。

「选择排序」看起来好像最没有用,但是如果在交换成本较高的排序任务中,就可以使用「选择排序」

3、插入排序(熟悉)

思路:每次将一个数字插入一个有序的数组里,成为一个长度更长的有序数组,有限次操作以后,数组整体有序。

class Solution {
    public int[] sortArray(int[] nums) {
        //注意 i 的初始值为 1,也就是第二个元素开始
        for (int i = 1; i < nums.length; ++i) {
            //待排序的值
            int temp = nums[i];
            //需要注意
            int j;
            for (j = i-1; j >= 0; --j) {
                //找到合适位置
                if (temp < nums[j]) {
                    nums[j+1] = nums[j];
                    continue;
                } 
                //跳出循环
                break;
            }
            //插入到合适位置,这也就是我们没有在 for 循环内定义变量的原因
            nums[j+1] = temp;
        }
        return nums;
    }
}

 

直接插入排序时间复杂度分析

最好情况时,也就是有序的时候,我们不需要移动元素,每次只需要比较一次即可找到插入位置,那么最好情况时的时间复杂度为O(n)。

最坏情况时,即待排序表是逆序的情况,则此时需要比较2+3+....+n = (n+2)(n-1)/2,移动次数也达到了最大值,3 +4+5+....n+1 = (n+4)(n-1)/2,时间复杂度为O(n^2).

我们每次插入一个数据的时间复杂度为O(n),那么循环执行 n 次插入操作,平均时间复杂度为O(n^2)。

直接插入排序空间复杂度分析

根据动画可知,插入排序不需要额外的存储空间,所以其空间复杂度为O(1)

直接插入排序稳定性分析

我们根据代码可知,我们只会移动比 temp 值大的元素,所以我们排序后可以保证相同元素的相对位置不变。所以直接插入排序为稳定性排序算法。

4 快速排序

1随机选取一个数(x = rand() % len + startIndex)作为基准;

2.让其他比它大的元素移动到数列一边,比他小的元素移动到数列另一边,从而把数组拆解成两个部分。

3.再对左右区间重复第二步,直到各区间只有一个数。

上图则为一次快排示意图,下面我们再利用递归,分别对左半边区间也就是 [3,1,2] 和右半区间 [7,6,5,8] 执行上诉过程,直至区间缩小为 1 也就是第三条,则此时所有的数据都有序。

代码:

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

/**
 * @param arr   排序数组
 * @param left  左哨兵
 * @param right 右哨兵
 */
public static void quickSort(int[] arr, int left, int right) {
	// 如果两个哨兵相遇,则结束循环
	if (left < right) {
		// 以最左边的数作为中心点(pivot)
		int i = left, j = right, pivot = arr[left];
		while (i < j) {
			// 中心点在最左边,所以此处要从右开始,向左找第一个小于pivot的数
			while (i < j && arr[j] >= pivot) {
				j--;
			}
			if (i < j) {
				// 退出循环有两种情况,i > j 或者 arr[j] < pivot
				// 如果能够进入该if语句说明是arr[j] < pivot,可以交换
                 // 交换时,覆盖了原来的arr[left],不过它的值已经保存在pivot变量中了
				arr[i++] = arr[j];
			}
			// 从左向右找第一个大于等于pivot的数
			while (i < j && arr[i] < pivot) {
				i++;
			}
			if (i < j) {
				arr[j--] = arr[i];
			}
		}
		arr[i] = pivot;
		/*
		至此完成了一次移位调换
		int pivot = arr[i];
		arr[i++] = arr[j];
		arr[j--] = arr[i];
		...
		...
		arr[i] = pivot;
		 */
        
		// 递归调用
		quickSort(arr, left, i - 1);
		quickSort(arr, i + 1, right);
	}
}

5. 希尔排序

改进的插入排序(优化:原数组的一个元素距离正确位置很远的情况)
先让间隔 h 的元素有序,再使得间隔为 h / 2,一直缩小,一直到 h = 1(此时数组有序)。

时间复杂度介于nlogn和n^2之间,空间复杂度1

 

 

6、归并排序(重点)
基本思路:借助额外空间,合并两个有序数组,得到更长的有序数组。例如:「力扣」第 88 题:合并两个有序数组。
算法思想:分而治之(分治思想)。「分而治之」思想的形象理解是「曹冲称象」、MapReduce,在一定情况下可以并行化。

 

 

 

 7 堆排序

 

 

 

8.基数排序

 

public void Radix() {
	int arr[] = {53, 3, 542, 748, 14, 214};//处理负数时需要改进
	
	int max = arr[0];
	for (int i = 1; i < arr.length; i++) {
	    if (max < arr[i])
	        max = arr[i];
	}
	int maxLength = (max + "").length();//得到最大数是几位数,妙!!!
	
	int[][] bucket = new int[10][arr.length];
	int[] bucketElementCounts = new int[10];//比如:bucketElementCounts[0] , 记录的就是  bucket[0] 桶的放入数据个数
	
	for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {//个十百...位
	
	    for (int j = 0; j < arr.length; j++) {
	        int digitOfElement = arr[j] / n % 10;//个十百...位
	        bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
	        bucketElementCounts[digitOfElement]++;
	    }
	    int index = 0;
	    for (int k = 0; k < bucketElementCounts.length; k++) {//遍历每一个桶
	        if (bucketElementCounts[k] != 0) {
	            for (int l = 0; l < bucketElementCounts[k]; l++) {//遍历k桶的数
	                arr[index++] = bucket[k][l];
	            }
	        }
	        bucketElementCounts[k] = 0;//第i+1轮处理后,需要将每个 bucketElementCounts[k] = 0 !!!!
	    }
	    
	}
	System.out.println(Arrays.toString(arr));
}

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值