排序算法对比一览图
类别 | 方法 | 数组实现 | 链表实现 | 稳定 | 适用场景 | ||
时间复杂度 | 空间复杂度 | 时间复杂度 | 空间复杂度 | ||||
插入排序 | 直接插入 | | ✅ | 在数列近乎可下降到O(n) | |||
shell排序 | - | - | ❌ | 延续了上一思想 | |||
交换排序 | 冒泡排序 | ✅ | |||||
快速排序 | 非递归: 递归: | 不考虑递归: | ❌ | 改进: 1)小partition上:插入排序 2)重复元素多:头尾指针 or 三路快排 | |||
选择排序 | 直接选择 | ✅ | |||||
堆排序 | - | - | ❌ | ||||
归并排序 | ✅ | ||||||
桶排序 | ? |
算法详解
快速排序
一个细节:在做partition时,为什么要从头尾两个方向向中间进行粗排序?
使得分割的左右两部分元素数量更均衡。只有一个方向遍历的话,等于key的元素分布会堆积在同一边;两个方向遍历的话,判断里都覆盖(或都不覆盖)等于key的元素,就能避免堆积。
数组实现
(可直接运行)
编程心得:两个指针实际含义是 一个指向坑位,一个找新坑位。
#include <iostream>
using namespace std;
void print(int nums[], int l, int r){
for(int i=l; i<=r; ++i){
cout<<nums[i]<<" ";
}
cout<<endl;
}
void sort(int nums[], int l, int r){
if(l>=r) return;
int key = nums[l];
int il = l, ir = r;
while (il<ir) {
while(il<ir && nums[ir]>=key){
--ir;
}
if(il<ir) nums[il++] = nums[ir];
while(il<ir && nums[il]<=key){
++il;
}
if(il<ir) nums[ir--] = nums[il];
}
nums[il] = key;
sort(nums, l, il-1);
sort(nums, il+1, r);
}
int main(int argc, const char * argv[]) {
int nums[] = {2,4,1,3,2};
print(nums,0,4);
sort(nums, 0, 4);
print(nums,0,4);
return 0;
}
链表实现
点1:链表中元素无法直接访问,导致找坑不能跳着找,因此解决办法是从前向后捋,记录前小后大的分界线,把未排序中小的往分界线放。
点2:key元素的坑位最后决定,但由于链表中无法直接访问,key一旦换开就很难找到,因此排序时先不要占用key的坑位,只在最后把头部的key和分界线上小的元素位置交换。
变量含义:p用来记录分界线上小于一边的元素,q用来找未排序中需要往前调动的小于的元素。
运行结果:实现一速度和内存略少于实现二。
实现一:链表不动,子链用首尾元素来判别。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* sortList(ListNode* head) {
if(head==NULL || head->next==NULL) return head;
sort(head, NULL);
return head;
}
void sort(ListNode* head, ListNode* end){
if(head==end || head->next == end) return;
int key = head->val;
ListNode *p = head, *q = head->next;
bool isok = true;
while(q!=end){
if(q->val < key){
p = p->next;
swap(p->val, q->val);
}
q = q->next;
}
swap(p->val, head->val);
sort(head, p);
sort(p->next, end);
}
void swap(int &p, int &q){
int tmp = q;
q = p;
p = tmp;
}
};
实现二:断开链表,子链是拆成单独的链。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* sortList(ListNode* head) {
if(head==NULL || head->next==NULL) return head;
ListNode *tmp = head->next;
ListNode *less = new ListNode(0);
ListNode *big = new ListNode(0);
ListNode *i = big, *j = less;
while(tmp!=NULL){
if(tmp->val >= head->val){
i->next = tmp;
i = i->next;
}else{
j->next = tmp;
j = j->next;
}
tmp = tmp->next;
}
i->next = NULL;
j->next = NULL;
ListNode *left = sortList(less->next);
ListNode *right = sortList(big->next);
if(left!=NULL){
i = left;
while(i->next!=NULL){
i = i->next;
}
i->next = head;
}
head->next = right;
return (left)?left:head;
}
};
归并排序
自顶向下:不断二分,逐个合并有序子列。
数组实现
(代码可直接运行)
#include <iostream>
using namespace std;
void print(int nums[], int len){
for(int i=0; i<len; ++i){
cout<<nums[i]<<" ";
}
cout<<endl;
}
void merge(int nums[], int l, int mid, int r){
int tmp[r-l+1];
int itmp = 0, ir = mid+1, il = l;
while(il<=mid && ir<=r){
if(nums[il]<=nums[ir]){
tmp[itmp] = nums[il++];
}else{
tmp[itmp] = nums[ir++];
}
++itmp;
}
while(il<=mid){
tmp[itmp++] = nums[il++];
}
while(ir<=r){
tmp[itmp++] = nums[ir++];
}
itmp = 0;
il = l;
while(itmp<r-l+1){
nums[il++] = tmp[itmp++];
}
print(nums,4);
}
void sort(int nums[], int l, int r){
if(l>=r) return;
int mid = (l + r) / 2;
sort(nums, l, mid);
sort(nums, mid+1, r);
merge(nums, l, mid, r);
}
int main(int argc, const char * argv[]) {
int nums[] = {4,2,1,3};
sort(nums, 0, 3);
print(nums, 4);
return 0;
}
链表实现
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* sortList(ListNode* head) {
if(head==NULL || head->next==NULL) return head;
ListNode *t1 = head, *t2 = head;
while(t2!=NULL && t2->next != NULL){
t2 = t2->next->next;
if(t2==NULL) break;
t1 = t1->next;
}
t2 = t1->next;
t1->next = NULL;
ListNode* l1 = sortList(head);
ListNode* l2 = sortList(t2);
return merge(l1, l2);
}
ListNode* merge(ListNode* l1, ListNode* l2){
if(l1==NULL) return l2;
if(l2==NULL) return l1;
ListNode* node = new ListNode(0);
ListNode *tail = node;
while(l1!=NULL && l2!=NULL){
if(l1->val <= l2->val){
tail->next = l1;
l1 = l1->next;
}else{
tail->next = l2;
l2 = l2->next;
}
tail = tail->next;
tail->next = NULL;
}
if(l1!=NULL){
tail->next = l1;
}
if(l2!=NULL){
tail->next = l2;
}
return node->next;
}
};
参考:
1.数组实现的排序算法对比:https://blog.youkuaiyun.com/yushiyi6453/article/details/76407640
https://blog.youkuaiyun.com/wuxinyicomeon/article/details/5996675