7.1 数组中第K个最大元素、​ K个一组翻转链表​、三数之和

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

输入: [3,2,1,5,6,4], k = 2
输出: 5
示例 2:

输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4

 使用快速排序进行快速查找

class Solution {
public:
    // 快速排序
    int Quick_Sort(vector<int>& arr, int begin, int end, int k){
    if(begin == end) return arr[begin];
    int tmp = arr[begin];
    int i = begin;
    int j = end;
    while(i != j){
        while(arr[j] <= tmp && j > i)
            j--;
        while(arr[i] >= tmp && j > i)
            i++;
        if(j > i){
            int t = arr[i];
            arr[i] = arr[j];
            arr[j] = t;
        }
    }
    arr[begin] = arr[i];
    arr[i] = tmp;
    if(i+1 == k) return arr[i];
    else if(i+1 > k) return Quick_Sort(arr, begin, i-1, k);
    else return Quick_Sort(arr, i+1, end, k);
}

    int findKthLargest(vector<int>& nums, int k) {
        return Quick_Sort(nums,0,nums.size()-1,k);
    }
};

关于快排,在循环结束条件的设置上有两种方式,一种为两个指针相遇即(i == j)为结束状态,此情况下,可以将双指针的移动收敛约束到一个位置,方便类似二分查找、快排定位等类似的定位功能上,也方便灵活处理更多的情况,相比之下,另一种交错结束状态,即(i < j),此种结束状态通常用于无需定位查找,只需要排序或者其他类似操作的情况,当然,此情况也可以用于定位,但是对于究竟使用 i 还是 j 的位置需要根据实际情况判断,因此,个人相对来说更喜欢相遇的结束状态

下面是两种情况下的快排代码:

void Quick_Sort(vector<int>& arr, int begin, int end){
    if(begin == end) return ;
    int tmp = arr[begin];
    int i = begin;
    int j = end;
    while(i != j){
        while(arr[j--] <= tmp && j > i)
        while(arr[i++] >= tmp && j > i)
        if(j > i){
            swap(arr[i],arr[j]);
        }
    }
    arr[begin] = arr[i];
    arr[i] = tmp;
    Quick_Sort(arr, begin, i-1);
    Quick_Sort(arr, i+1, end);
}
void Quick_Sort2(int *arr, int begin, int end) {
    if (begin >= end) return;
    int x = arr[begin], i = begin, j = end;  //定义初始化分界点和左右指针
    while (i < j) {
        while (arr[j] > x) j--;
        while (arr[i] < x) i++;    //移动左右指针
        if (i < j) swap(arr[i], arr[j]);
    }
    Quick_Sort2(arr, begin, j);      //递归处理左右两段
    Quick_Sort2(arr, j + 1, end);
}

 K个一组翻转链表

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]

 

输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]

 

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    pair<ListNode*,ListNode*> reverse(ListNode* head,ListNode* tail){
        // 递归反转链表 返回链表的头尾节点
        if (head == tail) return {head, tail};
        auto [newHead, newTail] = reverse(head->next, tail);
        head->next->next = head;
        head->next = nullptr;
        return {newHead, head};
    }
    ListNode* reverseKGroup(ListNode* head, int k) {
        // 取出K个节点,不足则直接返回
        ListNode* hair = new ListNode();
        hair->next = head;
        ListNode* pre = hair;
        ListNode* p = head;

        while(p){
            // 移动尾结点,判断剩余结点是否有K个
            ListNode* tail = pre;
            for(int i=0;i<k;i++){
                tail = tail->next;
                if(!tail) return hair->next;
            }
            ListNode* nextHead = tail->next;
            // 翻转链表并接入原链表
            auto [newHead, newTail] = reverse(p, tail);
            pre->next = newHead;
            newTail->next = nextHead;

            pre = newTail;
            p = nextHead;
        }
        return hair->next;
    }
};

三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

由于需要剔除不重复的三元组,且本身时间复杂度也并不小,因此在剪枝方面需要注意很多细节

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ans;
        int len  = nums.size();
        sort(nums.begin(),nums.end());
        for(int first=0;first<len-2;first++){
            if(nums[first] > 0) break;
            if (first > 0 && nums[first] == nums[first - 1]) {
                continue;
            }
            int second = first + 1;
            int third = len - 1;
            while(second < third){
                if(nums[second] + nums[third] == -nums[first]){
                    vector<int> tmp = {nums[first],nums[second],nums[third]};
                    ans.push_back(tmp);
                    while(second < third && nums[second]==nums[second+1])
                        second++;
                    while(second < third && nums[third]==nums[third-1])
                        third--;
                }
                if(nums[second] + nums[third] > -nums[first])              
                {
                    third--;
                }
                else
                {
                    second++;
                }
            }
        }
        return ans;
    }
};

另外一种消除重复三元组的方法就是使用set的数据结构,但是,由于unordered_set 无法将vector作为key值,但是set可以,但是set的自动排序会使得原本剪枝的复杂度再次提升而导致时间超时,下面仅为一种思路想法,实际运行时间是超时的(如果将上面的剪枝部分加入也是可以通过的) 

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        set<vector<int>> ans;
        int len  = nums.size();
        sort(nums.begin(),nums.end());
        for(int first=0;first<len-2;first++){
            int second = first + 1;
            int third = len - 1;
            while(second < third){
                if(nums[second] + nums[third] == -nums[first]){
                    vector<int> tmp = {nums[first],nums[second],nums[third]};
                    ans.insert(tmp);
                }
                if(nums[second] + nums[third] > -nums[first])              
                {
                    third--;
                }
                else
                {
                    second++;
                }
            }
        }
        vector<vector<int>> res;
        for(auto it:ans){
            res.push_back(it);
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值