力扣19:删除链表的倒数第N个结点
题目描述
给你一个链表,删除链表中的倒数第n个结点,并且返回链表的头节点
输入输出样式
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
输入:head = [1], n = 1
输出:[]
输入:head = [1,2], n = 1
输出:[1]
解法一常规思路(双指针的思路)
-
新建链表的各个结点
-
遍历查找链表中包含结点的个数
-
找出要寻找的结点对应在链表中的位数
-
对该结点的前驱和后继进行操作
-
但是这种算法,力扣在执行的时候会有问题,特别是链表结点个数为[1],输出要删除倒数最后一个结点的值的时候,会在在执行过程中有问题,但在自己编译器中能完美执行
#include<iostream> #include<vector> using namespace std; typedef struct ListNode * LNode; struct ListNode { int val; LNode next; //初始化 ListNode():val(0),next(nullptr){}; ListNode(int x):val(x),next(nullptr){}; ListNode(int x,ListNode *next):val(x),next(next){}; }; void createListNode(LNode Nlist) { LNode plist,qlist; plist=Nlist; int num; //输入将要创建结点的个数 cin>>num; for(int i=0;i<num;i++) { qlist=new ListNode(); cin>>qlist->val; qlist->next=nullptr; //使用尾插法衔接新插入的结点 plist->next=qlist; plist=qlist; } } void printListNode(LNode Nlist) { LNode plist; //跳过链表的头结点 plist=Nlist->next; while(plist) { cout<<plist->val<<" "; plist=plist->next; } cout<<endl; } int travelListNum(ListNode * Nlist) { ListNode * plist; int totalNum=0; plist=Nlist->next; while(plist) { totalNum++; plist=plist->next; } return totalNum; } ListNode *removeNthFromEnd(ListNode *head,int n) { int totalNum=travelListNum(head); //当要删除的倒数值小于1时,跳出 if(n<1) { return head; } int tempNum=totalNum-n+1; //tlist指向前驱结点 //plist指向当前结点 ListNode *tlist; ListNode *plist; int currentIndex=0; tlist=head; plist=head->next; //找到当前结点的前面那个结点 while(currentIndex+1<tempNum) { tlist=tlist->next; plist=plist->next; currentIndex++; } if(plist) { if(plist->next==nullptr) { tlist->next=nullptr; } else{ tlist->next=plist->next; } } return head; } int main() { //建立链表,并分配指针 LNode Nlist=new ListNode(); createListNode(Nlist); printListNode(Nlist); travelListNum(Nlist); int deleteIndex; cout<<"please input which index you want to delete"<<endl; cin>>deleteIndex; removeNthFromEnd(Nlist,deleteIndex); printListNode(Nlist); }
解法二计算链表长度
通过看解析,发现,对于连链表进行操作时,常用的技巧就是添加一个哑结点,使用他的next指针指向链表的头节点,此时就不需要对链表的头结点进行特殊判断。
int travelListNum2(ListNode * Nlist) { ListNode * plist; int totalNum=0; plist=Nlist; while(plist) { totalNum++; plist=plist->next; } return totalNum; } ListNode *removeNthFromEnd2(ListNode *head,int n) { int totalNum=travelListNum2(head); if(n<1) { return head; } int tempNum=totalNum-n+1; //新建哑结点,并直线跟原来的头节点 ListNode *dummy=new ListNode(0,head); ListNode *cur=dummy; for(int i=1;i<totalNum-n+1;i++) { cur=cur->next; } cur->next=cur->next->next; ListNode * ans=dummy->next; delete dummy; return ans; }
方法3使用堆栈的方法
//使用堆栈完成,因为时逆序的 ListNode *removeNthFromEnd3(ListNode *head,int n) { ListNode*dummy=new ListNode(0,head); stack<ListNode*>stk; ListNode* cur=dummy; //将指针推入堆栈 while(cur) { stk.push(cur); cur=cur->next; } for(int i=0;i<n;i++) { //出栈第n个指针 stk.pop(); } //倒数第n+1个指针在堆栈顶部 ListNode *pre=stk.top(); pre->next=pre->next->next; ListNode*ans=dummy->next; delete dummy; return ans; }
方法4使用快慢指针的方法
//使用快慢指针的方法 ListNode *removeNthFromEnd4(ListNode *head,int n) { ListNode *dummy=new ListNode(0,head); ListNode *fast=dummy; ListNode *slow=dummy; //fast要比slow超前n个位置 for(int i=0;i<n+1;i++) { fast=fast->next; } //以fast的指向作为判断基准 while(fast) { fast=fast->next; slow=slow->next; } //删除slow的下一个值 slow->next=slow->next->next; ListNode *ans=dummy->next; delete dummy; return ans; }