Leetcode 21-25题

合并两个有序链表

将两个升序链表合并为一个新的升序链表。

用两个指针指向两个链表的表头,然后每次比较一下哪个值小,将较小的节点接到答案后面即可。

ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
    auto dummy = new ListNode(), p = dummy;
    auto l1 = list1, l2 = list2;
    while(l1 && l2) {   // 当l1和l2都不为空才进入循环
        if(l1->val <= l2->val) {
            p->next = l1;   // 将l1节点接到答案尾部
            p = p->next;    // 将答案和l1后移
            l1 = l1->next;
        }else {
            p->next = l2;
            p = p->next;
            l2 = l2->next;
        }
    }
    if(l1)  p->next = l1;
    if(l2)  p->next = l2;
    return dummy->next;
}

括号生成

n n n对括号,生成所有可能并且有效的括号组合。

有效的就是要满足任意前缀中左括号的数量一定大于等于右括号的数量。

然后做DFS,每次递归时都要保证左括号数量大于等于右括号数量。

也就是说,只有左括号数量大于右括号数量时,才可以放右括号。(前提是还有右括号可以放)

vector<string> ans;

void dfs(int n, int l, int r, string path) {
    if(l == n && r == n) {          // 左右括号都放满了则输出
        ans.push_back(path);
        return ;
    }
    if(l != n)  dfs(n, l + 1, r, path + "(");       // 若有左括号就可以直接放
    if(r != n && l > r) dfs(n, l, r + 1, path + ")");   // 若有右括号且前缀中左括号数量严格大于右括号数量则可以放
}

vector<string> generateParenthesis(int n) {
    dfs(n, 0, 0, "");
    return ans;
}

合并K个升序链表

给定一个链表数组,每个链表均为升序排列,将所有链表合并到一个升序链表中。

本质上和合并两个有序链表相似,即每次找最小的节点并接到答案后面。但是这样每次找会有 O ( k ) O(k) O(k)的比较次数。

因此可以采用堆优化来查找最小值,即每次把每个链表都存一个节点到堆中,取最小值接到答案后面,是 O ( 1 ) O(1) O(1)的比较次数。

优先队列默认是大根堆,要重载小于号才能变成小根堆,如priority_queue, greater< int>> q;

若要重载其他数据结构的小于比较,需要仿函数来重载。

struct cmp {
    bool operator() (ListNode* a, ListNode* b) {    // 仿函数
        return a->val > b->val;                     // 重载为大于
    }
};
ListNode* mergeKLists(vector<ListNode*>& lists) {
    auto dummy = new ListNode(), p = dummy;
    priority_queue<ListNode*, vector<ListNode*>, cmp> heap;

    for(auto list : lists)  
        if(list)    heap.push(list);    // 样例2中有空链表
    while(!heap.empty()) {
        auto list = heap.top();         // 队头是最小的节点
        heap.pop();

        p->next = list;
        p = p->next;
        if(list->next)  heap.push(list->next);
    }
    return dummy->next;
}

两两交换链表中的节点

两两交换链表中相邻的节点,并返回交换后链表的头节点。

所有链表题,只要涉及到头节点的判断问题,都可以加一个虚拟头节点

head -> 1 -> 2 -> 3 -> 4 -> tail
head -> 2 -> 1 -> 3 -> 4 -> tail

要实现上述链表的两两交换,比如要交换1和2节点,需要将head指向2,将1指向3,将2指向1,实现这三步即可。

i -> j -> k -> l
交换j和k
i->next = k
j->next = k->next
k->next = j
ListNode* swapPairs(ListNode* head) {
    auto dummy = new ListNode(-1, head), p = dummy;
    while(p->next && p->next->next) {
        // p -> a -> b
        auto a = p->next, b = a->next;
        p->next = b;
        a->next = b->next;
        b->next = a;
        if(p->next->next)   p = p->next->next;
    }
    return dummy->next;
}

K个一组翻转链表

将链表每 k k k个节点一组进行翻转,返回修改后的链表。如果节点总数不是 k k k的整数倍,最后剩余的节点保持原有顺序。

head -> 1 -> 2 -> 3 -> 4 -> 5 -> tail
head -> 4 -> 3 -> 2 -> 1 -> 5 -> tail
head    1 <- 2 <- 3 <- 4    5 -> tail
		a    b
			 a    b
			 	  a    b

我们采取这样的策略,先改变内部的方向,内部都更改完之后,再更改两边的指向关系,如下代码和示意图:

ListNode* reverseKGroup(ListNode* head, int k) {
    auto dummy = new ListNode(-1, head);
    for (auto p = dummy;;) {
        auto q = p;
        for (int i = 0; i < k && q; i ++ ) q = q->next;
        if (!q) break;      // 以上为判断后面是否还有k个元素

        // head -> 1 -> 2 -> 3 -> 4 -> 5 -> tail
        //    p    a    b
        //    p         a    b
        //    p              a    b
        auto a = p->next, b = a->next;
        for (int i = 0; i < k - 1; i ++ ) {
            // a -> b -> c
            auto c = b->next;
            // a <- b  c
            b->next = a;
            // 移动到下一轮更改的位置
            a = b, b = c;
        }
        // head    1 <- 2 <- 3 <- 4    5 -> tail
        //    p    c              a    b
        // head    4 -> 3 -> 2 -> 1    5 -> tail
        //    p    a              c    b
        auto c = p->next;
        p->next = a, c->next = b;
        // head -> 4 -> 3 -> 2 -> 1 -> 5 -> tail
        //                        p
        p = c;
    }
    return dummy->next;
}

你可能感兴趣的:(Leetcode,leetcode,算法)