几种常见的排序算法
快速排序
基本思想
- 设定一个基准数,把小于基准数的数放到基准数前,把大于基准数的数放在基准数后
- 对基准数前的子数组递归排序,对基准数后的子数组递归排序
代码实现时,设置两个哨兵i和j,分别从left和right出动。注意要让j先出动,因为只有这样当i和j相遇时,说明是j先到达的,也就是arr[i]=arr[j]<pivot,这时交换arr[left]和arr[i]才能保证交换后基准左边的数都小于基准
代码实现
class Sort{
public void quickSort(int[] arr,int left,int right) {
if (left>right) {
return;
}
int pivot=arr[left];//pivot存的就是基准数
int i=left;
int j=right;
while (i!=j) {
//从右往左找小于基准数的数
while (arr[j]>=pivot && i<j) {
j--;
}
//从左往右找大于基准数的数
while (arr[i]<=pivot && i<j) {
i++;
}
if (i<j) {
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
//当i遇到j的时候,说明i(j)左边的数都小于arr[i]了,i右边的数都大于arr[i]了
arr[left]=arr[i];
arr[i]=pivot;
this.quickSort(arr, left, i-1);//递归出理基准左边的
this.quickSort(arr, i+1, right);//递归处理基准右边的
}
}
堆排序
基本思想
- 根据初始序列构建初始(大顶)堆,保证父节点都比它的孩子节点数值大
- 每次交换堆的第一个元素(根)和最后一个元素,输出最后一个元素(最大值),然后把剩下元素(除去最后一个元素)重新调整为大顶堆
代码实现
def HeadSort(input_list):
'''
Function:堆排序(升序)
Parameters:input_list-待排序列表
Returns:sorted_list-升序排列的列表
'''
def HeadAdjust(input_list, parent, length):
'''
Function:堆调整,调整为大顶堆
Parameters:
input_list-待排序列表
parent-堆的父节点
length-数组长度
Returns:无
'''
temp = input_list[parent]
child = 2*parent+1
while child < length:
if child+1<length and input_list[child]<input_list[child+1]:#找孩子节点中较大值
child+=1
if temp>=input_list[child]:
break
input_list[parent]=input_list[child]
parent=child#往下查看是否需要调整
child=2*parent+1
input_list[parent]=temp
if len(input_list)==0:
return []
sorted_list=input_list
length=len(sorted_list)
for i in range(0,length//2)[::-1]:#构造初始堆
HeadAdjust(sorted_list,i,length)
print('初始堆:',sorted_list)
for j in range(1,length)[::-1]:#堆调整
temp=sorted_list[j]
sorted_list[j]=sorted_list[0]
sorted_list[0]=temp
HeadAdjust(sorted_list,0,j)
print('第%d趟排序:'%(length-j),end=' ')
print(sorted_list)
return sorted_list
input_list=[1,3,4,5,2,6,9,7,8,0]
print('排序前:',input_list)
sorted_list=HeadSort(input_list)
print('排序后:',sorted_list)
归并排序
基本思想
归并其实是分治的思想。
通过递归实现链表归并排序,有以下两个环节:
- 分割cut环节:找到当前链表中点,并从中点将链表断开
- 使用快慢指针法,fast到链表尾时,slow在链表中点
- cut递归终止条件:只有一个节点,直接返回此节点
- 合并merge环节:将两个有序链表合并,转化为一个有序链表
- 建立辅助节点h作为头部
- 双指针分别指向两个有序链表头部,交替前进,直至添加完两个链表
- 返回辅助链表h的下个节点
代码实现
class Solution {
public ListNode sortList(ListNode head) {
if (head==null) {
return null;
}
if (head.next==null) {
return head;
}
ListNode slow=head;
ListNode fast=head.next;
while (fast!=null && fast.next!=null) {
slow=slow.next;
fast=fast.next.next;
}
ListNode left=head;
ListNode right=slow.next;
slow.next=null;
left=sortList(left);
right=sortList(right);
ListNode i=left;
ListNode j=right;
ListNode h=new ListNode();
ListNode cur=h;
while (i!=null && j!=null) {
if (i.val<j.val) {
cur.next=i;
ListNode t=i.next;
i.next=null;
i=t;
}else {
cur.next=j;
ListNode t=j.next;
j.next=null;
j=t;
}
cur=cur.next;
}
if (i!=null) {
cur.next=i;
}
if (j!=null) {
cur.next=j;
}
return h.next;
}
}
数组排序
public void mergeSort(int[] nums,int left,int right) {
//对nums从索引left到right进行排序
if (left==right) {
return;
}
int mid=(left+right)/2;
mergeSort(nums, left, mid);
mergeSort(nums, mid+1, right);
int[] res=new int[right-left+1];
int p1=left,p2=mid+1;
int p=0;
while (p1<=mid || p2<=right) {
if (p1>mid) {
res[p++]=nums[p2++];
}else if (p2>right) {
res[p++]=nums[p1++];
}else {
if (nums[p1]<nums[p2]) {
res[p++]=nums[p1++];
}else {
res[p++]=nums[p2++];
}
}
}
for (int i=0;i<res.length;i++) {
nums[left+i]=res[i];
}
}
493. 翻转对 - 力扣(LeetCode) (leetcode-cn.com)
327. 区间和的个数 - 力扣(LeetCode) (leetcode-cn.com)
基数排序
基本思想
假设原来一串数值如下所示:
72,22,93,43,55,14,65,39,81
首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:
分配过程:
- 设置长度为10数组cnt,cnt[i]表示这串数值中个位数为i的数的个数,cnt={0,1,2,2,1,2,0,0,0,1},个位数为1的有一个(81),个位数为2的有两个(72、22)
- 对cnt元素进行累加赋值,cnt[i]+=cnt[i-1],cnt={0,1,3,5,6,8,8,8,8,9},这样cnt[i]就表示个位为i的元素应该排序后的位置
- 对数组按照个位数大小按序放置buf={81,72,22,93,43,14,55,65,39}
接着再对buf按照十位排序,如果数字串中有三位数,需要对百位排序
代码实现
def radixSort(nums:List):
length=len(nums)
exp=1
maxVal=max(nums)
buf=[0 for i in range(length)]
while maxVal>=exp:
cnt=[0 for i in range(10)]
for i in range(length):
digit=nums[i]//exp%10
cnt[digit]+=1
for i in range(1,10):
cnt[i]+=cnt[i-1]
//注意这里要从后往前放置,比如对10位排序时
//十位相同的数,个位大的要放在后面
//cnt[digit]是递减的,也就是要先放个位大的,所以要从后往前放
for i in range(length-1,-1,-1):
digit=nums[i]//exp%10
buf[cnt[digit]-1]=nums[i]
cnt[digit]-=1
nums=[x for x in buf]
exp*=10
return nums