单向链表:查找第i个结点时只能从头结点开始,复杂度为O(n)。
题目:链表的创建,结点插入,删除结点,反转链表,倒数第k个结点...
1.向链表的尾部插入结点
typedef struct ListNode{int m_nvalue;ListNode* m_pNext;}; //往链表末尾中添加节点 void AddToTail(ListNode** pHead,int value) //特别注意pHead是一个指向指针的指针。因为往空链表中插入一个结点时,头指针是新插入的结点。此时头指针发生了改动,因此必须设置为指向指针的指针,否则出了该函数之后pHead仍然是一个空指针 { ListNode *pNew=new ListNode(); pNew->m_nvalue=value; pNew->m_pNext=NULL; if(*pHead==NULL) { *pHead=pNew; } else { ListNode* pNode=*pHead; while(pNode->m_pNext!=NULL) pNode=pNode->m_pNext; pNode->m_pNext=pNew; } }
2.删除结点
可以在O(1)复杂度下删除结点,最坏情况(删除的是尾结点)复杂度为O(n)。
//在链表中找到第一个含有某值的结点,并删除它。(只能从头向尾遍历,算法复杂度O(n),不能像数组那样采用索引) void RemoveNode(ListNode**pHead,int value) { if(pHead==NULL||*pHead==NULL) //*pHead==NULL表示空表 phead==NULL表示该指针未定义 return; ListNode* ToBeDeleted=NULL; if((*pHead)->m_nvalue==value) //如果头指针所指向的结点就是要删除的结点,则把头指针指向下一个结点,把头结点赋值为要被删除的点ToBeDeleted { ToBeDeleted=*pHead; *pHead=(*pHead)->m_pNext; } else //否则就去判断下一个结点是否应该被删除 { ListNode* pNode=*pHead; while(pNode->m_pNext!=NULL&&pNode->m_pNext->m_nvalue!=value) //下一个结点不是要删除的结点,继续往后移动 { pNode=pNode->m_pNext; } if(pNode->m_pNext!=NULL&&pNode->m_pNext->m_nvalue==value) //否则把下一个结点赋值为ToBeDeleted { ToBeDeleted=pNode->m_pNext; pNode->m_pNext=pNode->m_pNext->m_pNext; //当前结点的指针指向下一个结点的下一个结点 } } if(ToBeDeleted!=NULL) //找到了应该删除的结点时就删掉,否则结束寻找 { delete ToBeDeleted; ToBeDeleted=NULL; } }
//在O(1)时间之内删除链表结点 void DeleteNode(ListNode** pListHead,ListNode* pToBeDeleted) { if(!pListHead||!pToBeDeleted) return; //要删除的不是尾结点。如果要删除的是结点i, if(pToBeDeleted->m_pnext!=NULL) { ListNode* pNext=pToBeDeleted->m_pnext; pToBeDeleted->m_key=pNext->m_key; pToBeDeleted->m_pnext=pNext->m_pnext; delete pNext; pNext=NULL; } //链表只有一个结点,删除头结点 else if(*pListHead==pToBeDeleted) { delete pToBeDeleted; pToBeDeleted=NULL; *pListHead=NULL; } //链表中有多个结点,删除尾结点 { ListNode* pNode=*pListHead; while(pNode->m_pnext!=pToBeDeleted) { pNode=pNode->m_pnext; } pNode->m_pnext=NULL; delete pToBeDeleted; pToBeDeleted=NULL; } }
3.从尾到头打印结点,不改变链表结构
//从尾到头打印链表,不改变链表结构 void PrintListReverse(ListNode**pHead) { if(pHead==NULL||*pHead==NULL) return; ListNode* pNode=*pHead; vector<int> v_list; v_list.insert(v_list.begin(),pNode->m_nvalue); while(pNode->m_pNext!=NULL) { pNode=pNode->m_pNext; v_list.insert(v_list.begin(),pNode->m_nvalue); } vector<int>::iterator it=v_list.begin(); while(it!=v_list.end()) //对的 printf("%d ",*it++); /*while(it++!=v_list.end()) //这样写是从第2个数开始输出,并且指针溢出了。只有在for循环里面才保证循环体内的语句执行完了之后再自增,在while的判断里面是判断语句执行完了就自增。 printf("%d ",*it);*/ /*while(it++!=v_list.end()) //这样写是从第2个数开始输出,指针没有溢出。 printf("%d ",*it);*/ }
4.链表结构的反转
//链表结构的反转 void ListReverse(ListNode**pHead) { if(pHead==NULL||*pHead==NULL||(*pHead)->m_pNext==NULL) return; ListNode* p=*pHead; ListNode* q=*pHead; while(p->m_pNext!=NULL) { *pHead=p->m_pNext; p->m_pNext=(*pHead)->m_pNext; (*pHead)->m_pNext=q; q=*pHead; } }
void main() { ListNode** pHead=new ListNode*; //不赋值,就会出现指针未初始化的错误.原因是指针未赋值时,其值并不是0,而是随便指向了一处内存,当改变了此处内存的值时,可能会带来灾难性的错误 *pHead=NULL; int print_value,remove_value; for(int i=0;i<7;i++) { cin>>print_value; AddToTail(pHead,print_value); } PrintListReverse(pHead); cin>>remove_value; RemoveNode(pHead,remove_value); ListReverse(pHead); PrintListReverse(pHead); }
5.面试题:找到两个链表的第一个公共结点——剑指offer面试题37
思路1:用2个栈存储两个链表,从栈顶开始比较,相同的移除,移除的最后一个相同的结点就是两个链表的第一个公共结点。
思路2:首先分别遍历两个链表的长度,求得长度差k。设置2个指针,较长链表的指针先行k步,然后比较2个指针的值是否相同,如果不同,2个指针同时后移,直到找到第一个相同的指针为止。
思路1的代码:
//输入两个链表,找到他们第一个公共结点 #include<stdlib.h> #include<stdio.h> #include<stack> #include<iostream> using namespace std; struct ListNode { int m_key; ListNode* m_pnext; }; ListNode* findPublic(ListNode* const list1,ListNode* const list2) //指针值不能变 如果写成const ListNode* ,代表的是元素值不能变 { ListNode* PublicNode=new ListNode; if(list1==NULL||list2==NULL) return NULL; //这样写行吗? ListNode* plist1,*plist2; plist1=list1; //不能将const ListNode*分配到ListNode*类型的实体 plist2=list2; stack<ListNode*> stack1,stack2; while(plist1!=NULL) //把链表压栈 { stack1.push(plist1); plist1=plist1->m_pnext; } while(plist2!=NULL) { stack2.push(plist2); plist2=plist2->m_pnext; } while(stack1.top()==stack2.top()) //反过来比较,直到找到最后一个重合的值为止 { PublicNode=stack1.top(); stack1.pop(); stack2.pop(); } return PublicNode; } //生成一个链表的写法 void inputList(ListNode* &pfirst) //初始化时,要写成ListNode* pfirst=NULL { int p; ListNode* currentNode=NULL; while(cin>>p) { if(p==-1) break; ListNode* newNode=new ListNode; newNode->m_key=p; newNode->m_pnext=NULL; if(pfirst==NULL) { pfirst=newNode; currentNode=pfirst; } else { currentNode->m_pnext=newNode; currentNode=currentNode->m_pnext; } } }
6.合并2个排序的链表——剑指offer面试题17
//合并2个排序的链表 ListNode* Merge(ListNode* pHead1,ListNode* pHead2) { if(pHead1==NULL) return pHead2; else if(pHead2==NULL) return pHead1; ListNode* pMergedHead=NULL; if(pHead1->m_key<pHead2->m_key) { pMergedHead=pHead1; pMergedHead->m_pnext=Merge(pHead1->m_pnext,pHead2); } else { pMergedHead=pHead2; pMergedHead->m_pnext=Merge(pHead1,pHead2->m_pnext); } return pMergedHead; }