• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
有顶天の甲子园
博客园    首页    新随笔    联系   管理    订阅  订阅
【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; }

 

posted on 2017-07-03 18:29  生涯现役丶  阅读(269)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3