十大排序算法
- 选择排序、冒泡排序、插入排序、希尔排序。
- 归并排序和快速排序。
- 堆排序。
- 非比较排序:计数排序、基数排序、桶排序。
可以打开这个 网站,体验一下各种排序算法。
以「力扣」第 912 题:排序数组 为例,实现自己的排序算法,提交给这一题的测试用例,检验学习成果。
选择排序
思路:每一轮选取未排定的部分中 最小 的元素交换到未排定部分的最开头,经过若干个步骤,就能排定整个数组。即:先选出最小的,再选出第 2 小的,以此类推。
class Solution {
//在「力扣」运行超时
public int[] sortArray(int[] nums) {
int length = nums.length;
for(int i=0;i< length;i++){
// 用于标示最小值下标
int minIndex = i;
// 循环方式找到最小值
for (int j = i + 1; j < length; 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;
}
}
复杂度分析:
- 时间复杂度:O(N^2),这里 N 是数组的长度。
- 空间复杂度:O(1),使用到常数个临时变量。
冒泡排序
思路:
-
相邻的两个元素进行比较,把比较大的元素排在后面,这样遍历一轮下来,就可以找到这一轮循环中最大的那个元素,我们把这个过程形象地称之为「冒泡」。
-
由于每一轮循环都「冒泡」出一个这一轮循环最大的元素,所以上一轮循环的最后一个元素,不应该参加下一轮循环的比较了。
class Solution {
// 在「力扣」运行超时
public int[] sortArray(int[] nums) {
int length = nums.length;
for(int i= length -1 ;i >=0;i--){
// 先默认数组是有序的,只要发生一次交换,就必须进行下一轮比较
boolean sorted = true;
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j+1]) {
swap(nums,j,j+1);
sorted = false;
}
}
// 如果在内层循环中,都没有执行一次交换操作,说明此时数组已经是升序数组
if(sorted){
break;
}
}
return nums;
}
private void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
}
复杂度分析:
- 时间复杂度:O(N^2),这里 N 是数组的长度;
- 空间复杂度:O(1),使用到常数个临时变量。
插入排序
思路: 插入排序每一次将一个元素 插入 到它前面的有序数组中。
- 先暂存再后移:先暂存待插入元素,然后前面比暂存元素严格大的后移
class Solution {
// 在数组「几乎有序」的前提下,「插入排序」的时间复杂度可以达到 O(N);
public int[] sortArray(int[] nums) {
int length = nums.length;
for(int i= 1 ;i < length ;i++ ){
int j= i;
// 先暂存这个元素,然后之前元素逐个后移,留出空位
int temp = nums[j];
while(j >0 && nums[j -1] > temp){
nums[j] = nums[j-1];
j--;
}
nums[j] = temp;
}
return nums;
}
}
复杂度分析:
- 时间复杂度:O(N^2),这里 N 是数组的长度;
- 空间复杂度:O(1),使用到常数个临时变量。
归并排序
思路: