LC 19. 删除链表的倒数第N个结点

19. Remove Nth Node From End of List
请添加图片描述

方法一:双指针

算法思想:我们可以用两个指针,p , q,分别指向最后一个结点,和指向需要被删除结点的前驱结点。我们先让p指向第n个结点,之后p,q一起向后同步移动,间隔不变。直到p到达最后一个结点。这个时候q为我们需要删除结点的前驱结点。

// C语言版本

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

struct ListNode *removeNthFromEnd(struct ListNode *head, int n)
{
    struct ListNode *K = malloc(sizeof(struct ListNode));
    struct ListNode *p, *q;
    K->next = head;
    K->val = 0;
    p = K;
    q = K; //q指向被删除结点的前驱结点

    int i = 0;
    while (i != n)
    {
        p = p->next;
        i++;
    }

    while (p->next != NULL)
    {
        p = p->next;
        q = q->next;
    }
    p = q->next;
    q->next = p->next;
    free(p);
    struct ListNode *k = K->next;
    return k;
}

 

## python 版本

#Definition for singly - linked list.
#class ListNode:
#def __init__(self, val = 0, next = None):
#self.val = val
#self.next = next

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int):
        dummy = ListNode(0, head)
        first = head
        second = dummy
        for i in range(n):
            first = first.next

        while first:
            first = first.next
            second = second.next
        
        second.next = second.next.next
        return dummy.next

遇到的问题:

  1. 我们这里所给的head,不是头结点,里面也存储了信息,那么当结点数目为1个的时候,我们必须给它创建一个头结点。
  2. 用不同语言来做,会有些困难。多去换换语言。

方法二:栈

算法思想:我们可以利用栈的先进后出的思想,来做这到题目。将每个结点进栈,之后出栈,当出栈到第n个结点的时候,则这个时候这个出栈结点为我们要删除的结点,而栈顶元素为该需要被删除结点的前驱结点。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

typedef struct stacknode
{
    struct ListNode *node;
    struct stacknode *next;
} stacknode, *LiStack;

struct ListNode *removeNthFromEnd(struct ListNode *head, int n)
{
    struct ListNode *L = (struct ListNode *)malloc(sizeof(struct ListNode));
    stacknode *st = (stacknode *)malloc(sizeof(stacknode));
    L->val = 0, L->next = head;
    st->next = NULL;
    struct ListNode *p = L;

    while (p != NULL) //当p不为空的时候就进栈
    {
        stacknode *s = (stacknode *)malloc(sizeof(stacknode));
        s->node = p;
        s->next = st->next;
        st->next = s;
        p = p->next;
    }

    for (int i = 0; i < n; i++) //找到要删除的第n个位置
    {
        stacknode *q = st->next;
        st->next = q->next;
        free(q);
    }

    //这个时候我们找到 st->next->node 为我们要删除结点的前驱结点
    // pre->next 为我们要删除的结点

    struct ListNode *pre = st->next->node;
    struct ListNode *de = pre->next;
    pre->next = de->next;
    free(de);
    struct ListNode *l = L->next;
    return l;
}

遇到的问题:

  1. 为什么这里用链栈而不用顺序栈?个人见解,是这里并没有给出链表的长度,如果用顺序栈,不知道需要开多大的空间,容易造成浪费或者栈溢出。
  2. C语言用栈写,实属复杂,不同语言,解题效率也有很大差别,学会灵活运用。
  3. 链栈之前很少做过,不知道怎么进栈和出栈。这个很重要。

 

最后补充下链栈的相关代码:

#include <stdio.h>
#include <stdlib.h>
 
//栈的链式存储
typedef struct Linknode{
	int data;
	struct Linknode *next;
}Linknode,*LiStack; 
 
//初始化栈 
void InitStack(LiStack &L){
	L =(Linknode*)malloc(sizeof(Linknode));//创建头节点 
	L->next=NULL;//初始化为空	
}
 
//栈判空 
bool StackEmpty(LiStack L){
	if(L->next==NULL){
		printf("栈为空\n");
		return false;
	}
	else{
		printf("栈不为空\n");
		return true;
	}
}
 
//入栈 
bool Push(LiStack &L,int e){
 Linknode *s=(Linknode *)malloc(sizeof(Linknode));
 s->data=e;
 s->next=L->next;
 L->next=s; 
 return true;
 
}
 
//出栈 
bool Pop(LiStack &L,int &e){
	if(L->next==NULL){
		printf("栈已空\n");
		return false;
	}
	Linknode *p=L->next;
	e=p->data;
	L->next=p->next;//成链 
	printf("出栈元素值为%d\n",e);
	free(p);
	return true;
} 

bool GetTop(LiStack L,int x){
		if(L->next==NULL){
		printf("栈已空\n");
		return false;
	}
	Linknode *p=L->next;
	x=p->data;
	printf("栈顶元素值为%d\n",x);
	return true;
}
void PrintStack(LiStack L){
	Linknode *n=L->next;
	while(n!=NULL){
		printf("|___%d___|\n",n->data);
		n=n->next;
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值