zoukankan      html  css  js  c++  java
  • 链表系列文章(三)

    上一篇讨论了链表相关的几个有趣的问题,这一篇主要讨论与反转链表有关的问题

    基本数据结构:

    struct ListNode {
        int var;
        ListNode *next;
        ListNode( int v = 0 ) : var(v), next(NULL) { }
    };

    1.  反转链表

    方法一:堆栈法,利用栈后进先出的特性,将链表节点依次入栈,然后弹出并修改next指针

    时间复杂度O(n),空间复杂度O(n)

    1 ListNode *reverseList(ListNode *head) {
    2     if(!head || !head->next) return head;
    3     ListNode newhead(-1), *h = &newhead;
    4     std::vector<ListNode *> s;
    5     for( ; head; head = head->next ) s.push_back(head);
    6     while(!s.empty()) { h->next = s.back(); s.pop_back(); h = h->next; }
    7     h->next = NULL;
    8     return newhead.next;
    9 };

    方法二:头插法,创建一个新的头节点,然后遍历链表节点,将每个节点依次插入到头节点后

    时间复杂度O(n),空间复杂度O(1)

     1 ListNode *reverseList(ListNode *head) {
     2     if(!head || !head->next) return head;
     3     ListNode newhead(-1), *h = &newhead, *temp = NULL;
     4     while(head) {
     5         temp = head->next;
     6         head->next = h->next;
     7         h->next = head;
     8         head = temp;
     9     }
    10     return newhead.next;
    11 };

    方法三:前插法,把链表分为两部分,依次把后一部分的节点前插到前一部分

    时间复杂度O(n),空间复杂度O(1)

     1 ListNode *reverseList(ListNode *head) {
     2     if(!head || !head->next) return head;
     3     ListNode *prev = head, *curr = prev->next, *next = NULL;
     4     prev->next = NULL;
     5     while(curr) {
     6         next = curr->next;
     7         curr->next = prev;
     8         prev = curr;
     9         curr = next;
    10     }
    11     return prev;
    12 };

    2.  反转链表的[m, n]区间的节点

    和反转链表的区别是,我们必须保存反转后的尾指针,使其指向第n+1个节点

    方法一:堆栈法,相比上个问题,只需保存一下第n+1个节点地址即可,不再赘述

    方法二:头插法,时间复杂度O(n),空间复杂度O(1)

    ListNode *reverseList( ListNode *list, int m, int n ) { 
        ListNode *head = new ListNode();
        ListNode *phead = head;
        ListNode *curr, *prev;
        phead->next = list;
        n -= m;//需要反转的次数
        while( --m ) { phead = phead->next; } //phead->next指向第m个节点
        prev = phead->next;//prev指向反转区间的尾部
        curr = prev->next;//指向反转区间的头部
        while( n-- ) { 
            prev->next = curr->next;//这里的prev->next其实就是尾指针!!!
            curr->next = phead->next;
            phead->next = curr;//头插法
            curr = prev->next;  
        }   
        return head->next;
    }   

    3. 给定两个指针begin和end,反转begin->...->end之间的节点,返回反转后的倒数第一个节点指针

    方法参考 2.  反转链表的[m, n]区间的节点

     1 ListNode* reverseList(ListNode *prev, ListNode *begin, ListNode *end) {
     2     ListNode *end_next = end->next;
     3     for (ListNode *p = begin, *cur = p->next, *next = cur->next;
     4         cur != end_next;
     5         p = cur, cur = next, next = next ? next->next : NULL) {
     6         cur->next = p;
     7     }
     8     begin->next = end_next;//这里的begin->next其实就是尾指针!!!
     9     prev->next = end;
    10     return begin;
    11 }

    4. 小结

    反转链表本身比较简单,但是当要区间反转时,一定要牢记,不能忘了尾指针!!!

    由于本人水平有限,文中难免有不当和错误之处,欢迎大家批评指正,愿共同进步!!!

  • 相关阅读:
    dimensionality reduction动机---data compression(使算法提速)
    K-means:如何选择K(cluster的数目)
    python2和python3共存方法
    docker postgresql 数据库
    转:MySQL到底能支持多大的数据量?
    数据库jdbc链接:mysql, oracle, postgresql
    python获取参数列表
    转载:ubuntu系统启动顺序,常见系统服务说明
    使用postman开发testcases记录贴
    python gevent使用例子
  • 原文地址:https://www.cnblogs.com/zhuoyuan/p/4130380.html
Copyright © 2011-2022 走看看