AcWing 1451. 单链表快速排序

在这里插入图片描述

完整代码

采用快排实现链表的排序,比较难处理的地方是如何实现分隔,由于链表只能从前往后遍历不能从后往前遍历,那么
常规的分隔方法:

  • 最左边的元素为基准,两个指针一个指向最左边,一个指向最右边。
  • 先从右往左判断,右指针停在比基准小的元素,将此元素放在左指针指示的位置,
  • 再从左往右判断,将左指针停在比基准大的元素,将此元素放在右指针指示的位置
  • 直到左指针不再小于右指针,停止判断,该位置就是基准所放的位置

常规的分隔方法不适用,另外一种分隔方法:

  • 最左面的元素为基准,两个指针,一个指针p1指向小于基准元素的子序列的最后一个位置,一个指针p2指向待判别的元素,初始时p1指向基准元素,p2指向基准元素的下一个位置
  • 如果当前元素(p2所指向的元素)大于基准元素则p2向后移动
  • 否则, p1向后移动一个位置(此时指向大于基准元素的第一个元素),将p2所指的元素和p1所指的元素进行交换,p2再向后移动一个位置
  • 当p2移动到最后时,结束循环,将p1所指元素(小于等于基准元素的最后一个位置)和基准元素交换

链表实现起来比较困难,涉及到交换两个节点
如何交换链表的两个节点:

  • 需要记住两个节点的前驱指针
  • 如果两个节点是相邻节点,需要特别处理

交换完两个节点后需要将链表按照基准元素的位置分成两部分,分别进行快排,快排结束还需要将这两部分进行合并

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* quickSortList(ListNode* head) {
        ListNode * h = new ListNode(0);
        h -> next = head;
        return qsort(h)->next;
    }
private:
    ListNode* qsort(ListNode* phead){
        if(phead->next == NULL)
            return phead;
        ListNode *pivot = phead;
        ListNode *pleft = phead->next;
        while(pleft->next){
            if(pleft->next->val <= phead->next->val){
                pivot = pivot->next;
                if(pleft->next != pivot->next)
                    swap(pivot,pleft);
                else
                    pleft = pleft->next;
            }
            else
                pleft = pleft->next;
        }
        ListNode *nhead = new ListNode(0);
        if(phead != pivot){
            swap(phead, pivot);
            ListNode *next = pivot->next;
            nhead->next = next;
            pivot->next = NULL;
            qsort(phead);
            qsort(nhead);
        }
        else{
            //基准数已经放在了正确的位置,只需对基准数后面的排序
            qsort(phead->next);
        }   
        //merge
        ListNode * p1 = phead->next, * p2 = nhead->next, *h = new ListNode(-1), *p = h;
        while(p1 && p2){
            if(p1->val < p2->val){
                p->next = p1;
                p1 = p1->next;
            }
            else{
                p->next = p2;
                p2 = p2->next;
            }
            p = p->next;
        }
        while(p1){
            p->next = p1;
            p1 = p1->next;
            p = p->next;
        }
        while(p2){
            p->next = p2;
            p2 = p2->next;
            p = p->next;
        }
        return h;
    }
    void swap(ListNode* p1, ListNode *p2){
        if(p1-> next != p2){//两节点不相邻
            ListNode *cp1 = p1->next, *np1 = cp1->next;
            ListNode *cp2 = p2->next, *np2 = cp2->next;
            p1->next = cp2;
            cp2->next = np1;
            p2->next = cp1;
            cp1->next = np2;
        }
        else{
            ListNode  *np2 = p2->next, *np = np2->next;
            p1->next = np2;
            np2->next = p2;
            p2->next = np;
        }
        
    }
};

参考评论区的思想:
链表分隔的方法:

  • 遍历链表按照基准元素的大小拆分成三个不同的链表
  • 大于基准元素的链表,小于基准元素的链表,等于基准元素的链表

拆分完分别对左右拆分好的小于和大于基准元素的链表进行递归,最终再将三个链表合并

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* quickSortList(ListNode* head) {
        return quickSort(head);
    }
private:
    ListNode* getTail(ListNode* head){
        ListNode *p = head;
        while(p->next){
            p = p->next;
        }
        return p;
    }
    ListNode* quickSort(ListNode* head){
        if(!head || !head->next)
            return head;
        ListNode *left = new ListNode(-1), *right = new ListNode(-1), *mid = new ListNode(-1), *p = head;
        ListNode *lhead = left, *mhead = mid, *rhead = right;
        //分隔
        while(p){
            if(p->val < head->val){
                left = left->next = p;
            }
            else if(p->val == head->val){
                mid = mid->next = p;
            }
            else{
                right = right->next = p;
            }
            p = p->next;
        }
        left->next = NULL;
        mid->next = NULL;
        right->next = NULL;
        //递归,说明:设置了头节点避免了分割后一端为空的时候需要特别处理的步骤
        lhead->next = quickSort(lhead->next);
        rhead->next = quickSort(rhead->next);
        //合并
        getTail(lhead)->next = mhead->next;
        getTail(lhead)->next = rhead->next;
        
        
        return lhead->next;
    }
    
    
    
};
堆排序是一种基于堆数据结构的排序算法。它通过将待排序的序列构建成一个大顶堆(或小顶堆),然后依次将堆顶元素与最后一个元素交换,并重新调整堆,直到整个序列有序。 具体的堆排序算法如下: 1. 构建初始堆:将待排序序列构建成一个大顶堆。从最后一个非叶子节点开始,依次向前调整每个节点,使其满足大顶堆的性质。这一过程称为“下沉”操作。 2. 将堆顶元素与最后一个元素交换:将堆顶元素与待排序序列的最后一个元素进行交换,此时最后一个元素即为当前序列的最大值。 3. 重新调整堆:将交换后的堆顶元素进行“下沉”操作,使其满足大顶堆的性质。 4. 重复步骤2和3,直到整个序列有序。 下面是C++实现堆排序的代码: ```cpp #include <iostream> #include <vector> using namespace std; // 下沉操作 void heapify(vector<int>& nums, int n, int i) { int largest = i; // 初始化最大值为当前节点 int left = 2 * i + 1; // 左子节点 int right = 2 * i + 2; // 右子节点 // 如果左子节点大于根节点,则更新最大值 if (left < n && nums[left] > nums[largest]) { largest = left; } // 如果右子节点大于最大值,则更新最大值 if (right < n && nums[right] > nums[largest]) { largest = right; } // 如果最大值不是当前节点,则交换节点,并继续调整堆 if (largest != i) { swap(nums[i], nums[largest]); heapify(nums, n, largest); } } // 堆排序 void heapSort(vector<int>& nums) { int n = nums.size(); // 构建初始堆,从最后一个非叶子节点开始向前调整 for (int i = n / 2 - 1; i >= 0; i--) { heapify(nums, n, i); } // 依次将堆顶元素与最后一个元素交换,并重新调整堆 for (int i = n - 1; i > 0; i--) { swap(nums[0], nums[i]); heapify(nums, i, 0); } } int main() { vector<int> nums = {4, 2, 6, 8, 5, 7}; heapSort(nums); cout << "Sorted array: "; for (int num : nums) { cout << num << " "; } return 0; } ``` 以上是堆排序的C++实现代码,通过构建初始堆和重复交换堆顶元素的过程,最终实现了对序列的排序。时间复杂度为O(nlogn),其中n是序列的长度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值