LintCode解题记录 17.11.25 反转单链表

前言

这一周由于晚上课比较多,所以没有怎么做题。趁周五没有啥事来做几题。

35. Reverse Linked List

题目描述

正如其字面意思,反转一个单链表。要求* Do it in-place and in one-pass *

思路

如果是仅要输出单链表的反序,可以用一个递归,回溯的时候打印输出即可。
单纯只是觉得需要掌握这种递归的思路。

void ReversePrint(ListNode *head) {
    if (head == NULL) return;
    ReversePrint(head->next);
    printf("%d ", head->val);
}

当然这道题是需要修改链表结构的。其实修改链表结构也不是啥难事,画画图,改改链表节点的next指向问题就迎刃而解了。
来看第一种做法。
思路就是用两个指针curr和pre分别代表当前节点和前一节点。然后做以下步骤:

while curr != NULL
    t = curr->next; //存储原先curr的next域
    curr->next = pre; //修改curr的next域,使其指向前一节点
    pre = curr; // 跳至下一节点,此时curr就相当于下一次遍历时的pre节点
    curr = t; //t就是下一次遍历时的curr节点

这种方法简单明了,画画图一下子就明白了。注意这里curr初始化为head节点,pre要初始化为NULL。

来看第二种做法。第二种做法的主要思路就是不断将后面的节点插入到头结点之后。比如链表L->1->2->3->NULL,第一步操作就是将2插入到L的后面,变成L->2->1->3->NULL,第二步操作就是将3插入到L的后面,变成L->3->2->1->NULL。总结一下就是:

用curr来存储当前节点,初始化为头节点。
while curr->next != NULL
    p = curr->next;
    curr->next = p->next;
    p->next = L->next;
    L->next = p;

由于是从第二个节点开始往头节点插入的,所以实际上p应该是我要处理的节点。如果不理解,就画画图,自己笔画笔画链表的指向是怎么改的,就豁然开朗了。

36. Reverse Linked List II

题目描述

只要求反转链表中从m到n的部分,其中m和n事先给定。要求不变。

思路

利用35.题的第一种方法,我们首先找到第m节点m_node以及其前驱节点prem,然后定义n节点n_node和postn节点,分别初始化为m以及m->next。
然后按照第一种方法循环n-m次,循环结束后我们得到n节点此时位于链表的第n个节点,postn是其后继节点。接着我们只需要把首尾连接起来就得到了反转后的链表。
如果按照第二种方法,就把prem当做“头结点”,然后循环n-m次第二种方法即可。

代码
Version I:
    ListNode * reverseBetween(ListNode * head, int m, int n) {
        // write your code here
        if (m == n) return head;
        ListNode *L = new ListNode(-1);
        L->next = head;

        ListNode *p = L;
        for (int i = 1; i < m; i++) {
            if (NULL == p) return NULL;
            p = p->next;
        }

        ListNode *prem = p, *m_node = p->next;
        ListNode *n_node = m_node, *postn = m_node->next;

        for (int i = m; i < n; i++) {
            ListNode *t = postn->next;
            postn->next = n_node;
            n_node = postn;
            postn = t; 
        }

        prem->next = n_node;
        m_node->next = postn;

        return L->next;

    }
Version II:
    ListNode * reverseBetween(ListNode * head, int m, int n) {
        // write your code here
        if (m == n) return head;
        int p = 1;
        ListNode *curr = head;
        ListNode *L = new ListNode(-1);
        L->next = head;
        ListNode *res = L;
        while (p < m) {
            p++;
            L = curr;
            curr = curr->next;
        }

        ListNode *pnext;
        while (p < n) {
            pnext = curr->next;
            curr->next = pnext->next;
            pnext->next = L->next;
            L->next = pnext;
            p++;
        }        


        return res->next;

    }
总结

有的时候链表题光盯着代码看是很难看懂的,遍地next乱改。从一个例子出发,画画过程图,思路瞬间就会很清晰。包括写代码的时候我也是这样做的,你要说只在脑子里想就把节点间的连接关系想清楚,恐怕我还没有到这水平。

Reverse Nodes in K-Group

题目描述

按照k个节点为一组,将他们反转。如果剩下的个数不到k个,那么就保持原样。

思路

有了前面题目的基础上,这道题并不难。关键点仍然是画图理解。我们首先求得链表的长度,确定一共有几个group。然后只需要循环numGroup次,每一次做k次反转,最后就能得到答案。
当然也可以正面处理,每次处理k个节点,采用第一种方法。

代码
    ListNode * reverseKGroup(ListNode * head, int k) {
        // write your code here
        if (NULL == head) return NULL;
        int len = 0;
        ListNode *L = new ListNode(-1);
        L->next = head;

        ListNode *curr = L;
        while (curr->next) {
            curr = curr->next;
            len++;
        }

        int numOfGroups = len / k;

        ListNode *p, *pre;
        pre = L, curr = L->next;
        while (numOfGroups--) {
            //do something
            for (int i = 1; i < k; i++) {
                p = curr->next;
                curr->next = p->next;
                p->next = pre->next;
                pre->next = p;
            }
            //画图发现还需要下面两行操作:
            pre = curr;
            curr = curr->next;
        }

        return L->next;
    }

Swap Nodes in Pairs

题目描述

两两交换链表节点。

思路

上一题的简单版。

代码
    ListNode * swapPairs(ListNode * head) {
        // write your code here
        ListNode *L = new ListNode(-1);
        L->next = head;

        ListNode *pre = L;
        ListNode *one = L->next;
        ListNode *two;

        while (one && one->next) {
            two = one->next;
            one->next = two->next;
            two->next = pre->next;
            pre->next = two;

            pre = one;
            one = one->next;
        }

        return L->next;
    }

Swap Two Nodes in Linked List

题目描述

给定一个链表和两个值v1, v2,请你交换链表中值为v1和v2的两个节点。假设链表中没有重复的值,如果没有找到这两个节点,那么你
just do nothing.

思路

先在链表中找到v1和v2所在的节点以及他们的前驱节点。这里我假设p1始终在p2的前面,那么最终的形式就是p1->…->p2。
然后修改链表的next域即可,但是这里有一个特殊情况,就是p1->p2。需要在处理的时候单独说明。

代码
    ListNode * swapNodes(ListNode * head, int v1, int v2) {
        // write your code here
        ListNode *L = new ListNode(-1);
        L->next = head;

        ListNode *p1, *p2;
        ListNode *pre1, *pre2, *pre;
        pre = L;
        p1 = p2 = NULL;
        ListNode *curr = L->next;

        while (curr) {
            if (curr->val == v1 || curr->val == v2) {
                if (NULL == p1) {
                    pre1 = pre;
                    p1 = curr;
                } else {
                    pre2 = pre;
                    p2 = curr;
                }
            }
            pre = curr;
            curr = curr->next;
        }

        if (NULL == p1 || NULL == p2)
            return L->next;

        //下面这段代码之所以能写出来还是因为画图理解的。
        ListNode *t = p2->next;
        p2->next = p1 == pre2 ? p1 : p1->next;
        p1->next = t;
        pre1->next = p2;
        if (p1 != pre2) pre2->next = p1;

        return L->next;

    }

总结

链表题还是要多画图啊,兄dei.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值