几种基础排序算法及其比较
以下为个人学习总结 。如有不妥,热泪希望指正
1 冒泡排序
-
思路(从小到大):
(1)假设数据长度为N。总共要排序N-1轮。
(2)在每一轮排序中。相邻两个元素之间进行比较。不符合排序顺序的两个进行位置交换。每一轮结束后。教大的元素都被移到了最右侧。 -
冒泡思路示意图
-
代码示意
boolean isSorted= false;//是否已经完全排序好的标记
for(int i=0;i<nums.length-1;i++) {//这层循环控制总共要排序几轮
isSorted= true;//flag标记用于检查每一轮是否有元素进行交换。
//如果没有。说明都是排序好的。可以提前退出循环
for(int j=0;j<nums.length-1-i;j++) {//注意j的循环结束条件。因为从右往前的i个元素都是已经排序好的
if(nums[j]>nums[j+1]) {
int temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
isSorted= false;
}
}
if(isSorted) break;
}
- 复杂度分析
时间复杂度 | 空间复杂度 | 是否是稳定排序 |
---|---|---|
O(n^2) | O(1) | 是 |
- 冒泡排序的优化1 --减少总轮数和每轮的比较数
- 优化点1 :假设一个待排数组为【5,1,2,3,4】
如果是这种情况,那么在第一轮排序后,整个数组已经达到正确的顺序。
按原来的写法仍然是会比较接下来的3轮。 - 优化点2 :假设一个待排数组为【4,3,1,7,8,9,10】
如果是这种情况,在第一轮排序后,数组的元素排序情况是[3,1,4,7,8,9,10]。那么在第二轮排序的时候,元素1后面的数字们都已经按正确的位置排好了,是不需要一直比较到元素9的位置。而按原来的写法是会一致比较到底的
所以基于以上两个问题。优化冒泡排序
- 代码示意
public static void bubbleSort(int[] nums) {
boolean isSorted = true; //isSorted:优化点1:如果已经是排序好的,不需要再实行接下来的几轮
int goodSortedIndex = nums.length-1; //goodSortedIndex:优化点2:如果在其中某一轮的比较过程中,已经是排序好的,则不需要一直比较到最后。
for(int i=0;i<nums.length-1 ;i++) {//这层循环控制总共要排序几轮
isSorted = true;
int m = 0;
for(int j=0;j<goodSortedIndex;j++) {//注意j的循环结束条件。因为从右往前的i个元素都是已经排序好的
if(nums[j]>nums[j+1]) {
int temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
isSorted = false;
m =j;//记录当前比较的位置
}
}
goodSortedIndex = m;
if(isSorted) break;
}
}
2 插入排序
- 思路:
将数组可以看成两部分。一部分是有序的。一部分是无序的。
从无序中拿数字,在有序部分中找到合适位置插入。
- 复杂度分析
时间复杂度 | 空间复杂度 | 是否是稳定排序 |
---|---|---|
O(n^2) | O(1) | 是 |
- 代码如下
public void insertSort(int[] nums) {
for(int i =1;i<nums.length;i++) {
int value = nums[i];
int j =i;
for(;j>1;j--) {
if(value <nums[j-1]) { //如果左边的元素比value大,左边元素往右移。
nums[j] = nums[j-1];
}else { //如果左边的元素较小,不需要再比较了,直接调出本轮循环
break;
}
}
nums[j] = value;//将value插入正确的位置
}
}
3 选择排序
- 思路:
将数组分为两部分,一部分为已排区,一部分为待排区。
每次从待排区找到最小的值。交换到已排区
- 复杂度分析
时间复杂度 | 空间复杂度 | 是否是稳定排序 |
---|---|---|
O(n^2) | O(1) | 否(因为交换位置会使相对位置发生变化) |
- 代码如下
public void chooseSort(int[] nums) {
for(int i = 0;i<nums.length-1;i++) {
int minIndex = i;
for(int j = i+1;j<nums.length;j++) {
if(nums[j] <nums[minIndex]) {
minIndex = j;
}
}
//一轮比较完后,将最小的元素移动到有序区的最后面
int temp = nums[i];
nums[i] =nums[minIndex];
nums[minIndex] = temp;
}
}
4 归并排序
- 思路:将数据递归一分为二,直到不能再分解时,两个小数组进行有序合并。分而治之
- 图示
/**
* 归并排序
* @param nums
* @param start
* @param end
*/
public void mergeSort(int[] nums,int start,int end) {
if(start == end) {//数据不能再分的出口
return;
}
//将待排序数据一分为二、
int mid = start+(end-start)/2;//分界的中点
mergeSort(nums,start,mid);
mergeSort(nums,mid+1,end);//注意这里mid+1
//然后合并
merge(nums,start,mid,end);
}
/**
* 归并排序之合并
* @param nums
* @param start
* @param end
*/
private void merge(int[] nums, int start, int mid,int end) {
System.out.println("归并排序开始");
int[] temp = new int[end-start+1];//申请一个临时数组
int t =0;
//i指针前一半待排序的数组位置,j指针代表后一半待排序的数组的位置
int i = start;
int j = mid+1;
for(;i<=mid && j <=end;) {
if(nums[i] <=nums[j]) {
temp[t]=nums[i++];
}else {
temp[t] = nums[j++];
}
t++;
}
//判断哪半组数据还剩余
int rest =0;
if(j != end+1) {
rest = j;
}else {
rest = i;
}
//将属于的数据加入到temp中
while(t<=end-start) {
temp[t++] = nums[rest++];
}
//将temp数据的数组赋值到nums中
t--;
while(t>=0) {
nums[end--] =temp[t--];
}
}
- 复杂度分析
时间复杂度 | 空间复杂度 | 是否是稳定排序 |
---|---|---|
O(nlogn) | O(n) | **是 |
5 快速排序
- 思路:定义一个基准点pivot。然后根据基准点将数据分成两半。
- 图解:
- 代码如下
/**
* 快速排序
* @param nums
* @param start
* @param end
*/
public void quickSort(int[] nums,int start,int end) {
if(start >= end) {
return;
}
int partition = partition(nums,start,end);//分区,返回最后的分区节点
quickSort(nums,partition+1,end);
quickSort(nums,start,partition-1);
}
private int partition(int[] nums,int start, int end) {
int left = start;
int right = end;
int pivot = nums[start];
while(left < right) {
//右指针往左移,直到找到数字比pivot小时,停下
//这种解法下,必须右指针先动。
while(left<right && nums[right] >pivot) {
right--;
}
//左指针往右移,直到找到数字比pivot大时,停下
while(left<right && nums[left]<=pivot) {
left++;
}
//交换左右两个指针指向的值
if(left<right) {
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
}
}
//将pivot指针的值和左指针的值交换
nums[start] = nums[left];
nums[left] = pivot;
return left;
}
- 复杂度分析
时间复杂度 | 空间复杂度 | 是否是稳定排序 |
---|---|---|
O(nlogn) | O(1) | 否 |