• 【LeetCode】Remove Nth Node From End of List


    给定一个链表,删除从链表尾数起第n个节点,并返回头节点。

    e.g. 

    给定链表:1 -> 2 -> 3 -> 4 -> 5,n = 2

    删除倒数第二个节点后的链表: 1 -> 2 -> 3 -> 5

    我的笨方法:

    原理是判断要删除的节点为从头数起第 count 个节点,然后判断是否为头节点,进行删除。

     1     int length(ListNode* head) {
     2         int n = 0;
     3         ListNode *p = head;
     4         while (p) {
     5             p = p -> next;
     6             n++;
     7         }
     8         return n;
     9     }
    10     
    11     ListNode* removeNthFromEnd(ListNode* head, int n) {
    12         int count = length(head) - n + 1;
    13         if (count == 1) {
    14             ListNode *p = head;
    15             head = head -> next;
    16             delete p;
    17         } else if (count-- > 1) {
    18             ListNode *p = head, *q;
    19             while (count) {
    20                 q = p;
    21                 p = p -> next;
    22                 count--;
    23             }
    24             q -> next = p -> next;
    25             delete p;
    26         }
    27         return head;
    28     }

    看答案看到一个使用二级指针的方法,惊为天人。

     1 ListNode* removeNthFromEnd(ListNode* head, int n) {
     2     ListNode** t1 = &head, *t2 = head;
     3     // t2向后移n个节点
     4     while (n--) t2 = t2->next;  
     5     // 使t2移到最后一个节点的next,即NULL
     6     // t1指向那个指向待删除节点的指针,即指向待删除节点的上一个节点的next
     7     while (t2 != NULL) {
     8         t2 = t2->next;
     9         t1 = &((*t1)->next);
    10     }   
    11     t2 = *t1;   // t2指向待删除节点
    12     (*t1) = t2->next;
    13     delete t2;
    14     return head;
    15 }

    原理:

        该方法 4 - 11 行直接正序找到倒数第 n 个节点。

        如本例 n = 2 时,t2 先向后移动 2 个节点到 3 ,然后 t1 和 t2 一起向后移动,当 t2 移动到最后的 NULL 时,t1 指向那个【指向待删除节点 4 的指针】,即指向待删除节点 4 的上一个节点 3 的next。最后 t2 = *t1; 则让 t2 指向待删除节点 4 。

    二级指针在链表中的使用

    参考 http://coolshell.cn/articles/8990.html

        一般人以及多数教材在实现删除链表节点的函数的时候,维护了一个 prev 指针,然后删除当前指针 cur 。

    while (cur)  
    {  
        if (cur->data == tdata) {  /* 删除data == tdata的链表节点 */
            /* 如果待删除的节点是链表头 */  
            if (cur == head) {
                head = cur->next;  
            } else {   /* 如果待删除的节点不是链表头 */
                prev->next = cur->next;  
            }  
            delete cur;    
        }  
        prev = cur;  
        /* 遍历所有节点 */  
        cur = cur->next;  
    }  

    这种一般方法是通过 head 指向的结构体实例以及所有 next 指针指向的结构体实例来遍历链表的。

        而如果使用二级指针,可以把头结点和其他节点等效对待,为什么呢?因为链表中每个节点都由前面一个节点的 next 指针来指向,而头结点是由 head 指针指向的,所以 head 指针和其他节点的 next 指针扮演着完全相同的角色。因此二级指针 **cur 使用 head 和所有 next 指针来遍历链表

    while (*cur) {  
        entry = *cur;  
        if (entry->data == tdata) {  
            /* 删除 entry 节点 */  
            *cur = entry->next;  
           delete entry;  
        }  
        /* 遍历所有节点的指针 */  
        cur = &(entry->next);  
    } 

    在C++中,变量名的意义是拷贝一个值。之前的常见做法,cur = head->next,仅仅是把指针 head->next 的值copy给了指针 cur,使得 cur 可以访问链表的下一个节点;而声明为二级指针的 cur,*cur 不是 head->next 的copy值,它“就是” head->next 这个变量名的别名,它获得的是 head->next 这个变量的读写权。

    不过个人认为这两种方法没有孰优孰劣,都声明了 2 个指针。。。

    最有用的还是用两个指针正序遍历链表找到倒数第 n 个节点,因此可以写出最简洁的一般算法。

    C++实现:

     1 ListNode* removeNthFromEnd(ListNode* head, int n) {
     2     ListNode *t1 = head, *t2 = head;
     3     while (n--) t2 = t2->next;
     4     if (!t2)    return head->next;  // 要删除的是头节点  
     5     t2 = t2->next;    // t2再后移一个节点,保证t1、t2之间间隔n个节点,这样t2变为NULL时,t1为待删除节点的前驱
     6     while (t2) {
     7         t1 = t1->next;
     8         t2 = t2->next;
     9     }
    10     t2 = t1->next;    // t2为待删除节点
    11     t1->next = t2->next;
    12     delete t2;
    13     return head;
    14 }

    PS:

    C++对应的Java代码

    ListNode *t1 = head;          ListNode t1=head;
    head->next                    head.next
    { t2 = t1->next; t1->next = t2->next; ===> Java可直接写成 t1.next = t1.next.next; 不会有内存泄漏 delete t2; }
  • 相关阅读:
    构建调试Linux内核网络代码的环境MenuOS系统
    stm32内存管理
    STM32CubeMx——ADC多通道采集
    STM32CubeMx——串口使用DMA收发数据
    STM32CubeMx——串口收发
    stm32CubeMx+TrueSTUDIO+uc/os-III移植开发(二)
    stm32CubeMx+TrueSTUDIO+uc/os-III移植开发(一)
    STM32F103RCT6移植到STM32F103C8T6注意事项
    关于STM32F103系列从大容量向中容量移植的若干问题
    KEIL软件中编译时出现的Error L6200E: symbol multiply defined ...的解决方法
  • 原文地址:https://www.cnblogs.com/wayne793377164/p/7112539.html
Copyright © 2020-2023  润新知