单向扫描快排,双向扫描快排与非递归快排

本文介绍了一种常用的排序算法——快速排序,并提供了三种不同的实现方式:单向扫描、双向扫描及非递归版本。通过具体代码示例展示了如何利用这些方法对整数数组进行排序。
import java.util.Stack;


public class QuickSort {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int unsort[]={4,3,5,6,2,1},i;
quickSort3(unsort,0,5);
for(i=0;i<6;i++)
System.out.print(unsort[i]+",");
}

//单向扫描快排
static void quickSort(int p[],int left,int right){
int i,j,x,temp,k;
if(left<right){
i=left-1;
x=p[right];
for(j=left;j<right;j++){
if(p[j]<=x){
i++;
temp=p[i];p[i]=p[j];p[j]=temp;
}
}
temp=p[i+1];p[i+1]=x;p[right]=temp;
quickSort(p,left,i);
quickSort(p,i+2,right);
}
}

//双向扫描快排
static int partition(int p[],int left,int right){
int temp=p[left];
while(left<right){
while(left<right&&p[right]>=temp)
right--;
p[left]=p[right];
while(left<right&&p[left]<=temp)
left++;
p[right]=p[left];
}
p[left]=temp;
return left;
}
static void quickSort2(int p[],int left,int right){
int mid;
if(left<right){
mid=partition(p,left,right);
quickSort2(p,left,mid-1);
quickSort2(p,mid+1,right);
}
}

//非递归快排
static void quickSort3(int p[],int left,int right){
int mid;
Stack<Integer> stack=new Stack<Integer>();
if(left<right){
mid=partition(p,left,right);
if(left<mid-1){
stack.push(left);
stack.push(mid-1);
}
if(right>mid+1){
stack.push(mid+1);
stack.push(right);
}
while(!stack.empty()){
int high=stack.pop();
int low=stack.pop();
mid=partition(p,low,high);
if(low<mid-1){
stack.push(low);
stack.push(mid-1);
}
if(high>mid+1){
stack.push(mid+1);
stack.push(high);
}
}
}
}
}
在链表上实现**快速排序(Quick Sort)** 是可行的,但由于链表无法随机访问,不能像数组那样通过下标快速分区。我们需要根据链表的特点进行调整。 --- ## ✅ 快速排序的基本思想(回顾) 1. 选择一个基准值(pivot) 2. 将小于 pivot 的元素放在左边,大于等于的放在右边(分区操作) 3. 对左右两部分递归排序 在数组中这很高效,但在**单向链表**中: - 无法从后往前遍历 - 分区时需要重新组织指针 - 不支持随机访问 → 找 pivot 需要遍历 尽管如此,我们仍然可以实现一个高效的链表快排。 --- ## ✅ 链表快排的核心思路 我们将使用 **“三路分区” + 递归合并”** 的方式: ### 🔧 步骤: 1. 取第一个节点为 `pivot` 2. 遍历链表,将节点分为三个部分: - `less`: 值 < pivot 的节点 - `equal`: 值 == pivot 的节点(包含 pivot 自身) - `greater`: 值 > pivot 的节点 3. 递归对 `less` 和 `greater` 排序 4. 拼接结果:`sorted_less → equal → sorted_greater` > ⚠️ 注意:我们不真正“交换”节点,而是**重用节点对象并重组 next 指针** --- ## ✅ Python 实现代码 ```python # Definition for singly-linked list. class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next class Solution: def sortList(self, head: ListNode) -> ListNode: if not head or not head.next: return head return self.quickSort(head) def quickSort(self, head: ListNode) -> ListNode: if not head or not head.next: return head # Step 1: 创建三个哑节点来管理三个分区 dummy_less = ListNode(0) dummy_equal = ListNode(0) dummy_greater = ListNode(0) less = dummy_less equal = dummy_equal greater = dummy_greater # Step 2: 选 pivot(取头节点值) pivot = head.val # Step 3: 遍历原链表,进行三路划分 cur = head while cur: if cur.val < pivot: less.next = cur less = less.next elif cur.val == pivot: equal.next = cur equal = equal.next else: greater.next = cur greater = greater.next cur = cur.next # 断开尾部 less.next = None equal.next = None greater.next = None # Step 4: 递归排序 less 和 greater 部分 sorted_less = self.quickSort(dummy_less.next) sorted_greater = self.quickSort(dummy_greater.next) # Step 5: 拼接三部分 # 找到 sorted_less 的尾部 final_head = sorted_less if sorted_less else dummy_equal.next tail = self.getTail(sorted_less or dummy_equal) # 连接 equal 和 greater tail.next = dummy_equal.next self.getTail(dummy_equal).next = sorted_greater return final_head def getTail(self, node: ListNode) -> ListNode: """获取链表最后一个节点""" while node and node.next: node = node.next return node ``` --- ## 🔍 代码详解 | 步骤 | 说明 | |------|------| | `dummy_less/equal/greater` | 作为临时头节点,方便构建三个子链表 | | `pivot = head.val` | 选取首节点值作为基准(也可随机选,但链表难随机) | | 遍历原链表 | 把每个节点挂到对应的链表上 | | `less.next = None` 等 | 切断避免成环 | | 递归调用 | 对 `less` 和 `greater` 子链表排序 | | `getTail(...)` | 找到有序链表末尾以便拼接 | --- ## 📌 示例演示 输入:`4 → 2 → 1 → 3 → 5` 1. pivot = 4 - less: `2 → 1 → 3` - equal: `4` - greater: `5` 2. 递归排序 less 得到 `1 → 2 → 3` 3. 拼接:`1→2→3 → 4 → 5` 输出:`1 → 2 → 3 → 4 → 5` --- ## ✅ 时间与空间复杂度分析 | 指标 | 分析 | |------|------| | **平均时间复杂度** | $O(n \log n)$ | | **最坏时间复杂度** | $O(n^2)$(当每次 pivot 极端偏斜,如已排序) | | **空间复杂度** | $O(\log n)$(递归栈深度)+ $O(1)$ 辅助指针 | | **是否稳定** | 否(相同元素相对顺序可能改变) | > 💡 提示:为了改善性能,可以选择“中位数附近”的节点作为 pivot(需要遍历),但实现复杂。 --- ## ❗ 注意事项 - 单向链表不适合传统双指针快排(如 Lomuto 或 Hoare 分区),因为无法反向移动。 - “三路分区”能有效处理重复元素。 - 必须切断各段末尾(`next = None`),否则会形成环或越界。 - 如果链表很长且接近有序,快排退化严重,建议改用归并排序。 --- ## ✅ 优化建议(进阶) | 优化方向 | 方法 | |--------|------| | 减少递归深度 | 改为迭代式快排(使用栈模拟) | | 更好 pivot | 使用“三数取中”策略(需先遍历找末尾) | | 小数组切换 | 当长度 < 10 时改用插入排序 | --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值