链表部分总体小节
1:链表部分,要学会多创建一些头指针,多个之间的互补使用衔接
2:学会巧使用pre->next=head,便于做到对后续的统一判断
3:快指针、慢指针的问题,快指针同时走两步是慢指针的2倍,可以用来判断是否有环,有环总是能撵上的
4:倒数第几个的问题,快指针先走第n步,则慢指针再走,两个链表可以很快地定位到倒数第n位
5:链表之间的赋值关系要慎重,因为很容易造成链表混乱,学习后面的方法,一种是直接对链表反转,一种是单个对内部的进行翻转,第二种自己理解花了很久,采用新建temp重新创造关系,以及指针next移动根部不动的方法自己要多深入体会
6:题目1中的算法要记住
7:要记得对链表的末尾加NULL不要忘记
8:多个头节点有的时候就是为了维护同一个链表,所有通常情况下都是不对head直接进行操作的,需要进行保留,只要少部分递归的情况需要直接用head,再者就是pre节点,最后返回pre->next的问题,虽然最开始定义的是pre->next=head, cur=head但是并不代表最终运算完返回pre->next还是等于head了,因为head会一直不变,但是pre->next也许会根据实际情况为空也是可能的,还是要看代码编写情况再确定!!!
总而言之~~~~~链表确实是比树会难一些,树基本不会修改删除一些树之间的关系,几乎就是求深度,最短路径等等,而链表则就要多一些插入删除的操作,其因为其简洁的形式,也多了一些更多结合使用的功能
题目1(链表的环入口点)
收获
1:这道题最重要的思想是第一次先找到相遇点,接下来快的从头开始遍历到入口的距离=相遇点到入口的距离+(k-1)圈圆环长度
代码
class Solution {
public:
// 算法的思路很重要!!!
ListNode *iscycle(ListNode *head){
if(head==NULL)
return NULL;
ListNode *fast=head;
ListNode *slow=head;
while(fast!=NULL&&fast->next!=NULL){
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
return slow;
}
return NULL;
}
ListNode* EntryNodeOfLoop(ListNode* pHead) {
ListNode *slow=iscycle(pHead);
if(slow==NULL)
return NULL;
ListNode *fast=pHead;
while(fast!=slow){
fast=fast->next;
slow=slow->next;
}
return slow;
}
};
题目2(删除链表的倒数第n个节点)
收获
1:这道题总体的思想还是快链表先走,慢链表记录前一个位置方便衔接
2:自己独立完成,较好
代码
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
class Solution {
public:
/**
*
* @param head ListNode类
* @param n int整型
* @return ListNode类
*/
ListNode* removeNthFromEnd(ListNode* head, int n) {
// write code here
ListNode *fast=head;
//快链表先走n个,之后慢链表从头遍历并记住前一个选项用于删除
for(int i=0;i<n;i++){
fast=fast->next;
}
//如果没有这句话的话后期的pre没有初值不能使用next函数!!
if(fast==nullptr)
return head->next;
//这里的head->next通常就是删除倒数第一个,所以直接返回第二个
ListNode *slow=head;
ListNode *pre;
while(fast){
pre=slow;
fast=fast->next;
slow=slow->next;
}
//循环结束pre指向的就是第n-1个节点!!
pre->next=slow->next;
return head;
}
};
题目3(链表相加2)
收获
1:这道题在中间记录的过程中,本打算直接对得到的结果进行相反,忘记记录最初的位置了,导致不能很好的翻转是一个小失误,后来自己也及时改进
2:觉得自己对链表的排序合并,以及相加减移动这方面已经较熟悉了
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
/**
*
* @param head1 ListNode类
* @param head2 ListNode类
* @return ListNode类
*/
ListNode *reverseLink(ListNode *head){
if(head==NULL)
return NULL;
ListNode *pre=NULL;
ListNode *cur=head;
while(cur){
ListNode *temp=cur->next;
cur->next=pre;
pre=cur;
cur=temp;
}
return pre;
}
ListNode* addInList(ListNode* head1, ListNode* head2) {
// write code here
if(head1==nullptr)
return reverseLink(head2);
if(head2==nullptr)
return reverseLink(head1);
//将前两个链表反转
ListNode *first=reverseLink(head1);
ListNode *second=reverseLink(head2);
int jinwei=0;
ListNode *third=nullptr;
//如果不用这个result记录的话最后的third的链表方向反了,并且在创建头链表的时候要记得创建
//第一个的特殊用法~
ListNode *result;
while(first&&second){
if(third==nullptr){
third=new ListNode((first->val+second->val+jinwei)%10);
result=third;
jinwei=(first->val+second->val+jinwei)/10;
}else{
third->next=new ListNode((first->val+second->val+jinwei)%10);
third=third->next;
jinwei=(first->val+second->val+jinwei)/10;
}
first=first->next;
second=second->next;
}
while(first){
third->next=new ListNode((first->val+jinwei)%10);
jinwei=(first->val+jinwei)/10;
third=third->next;
first=first->next;
}
while(second){
third->next=new ListNode((second->val+jinwei)%10);
jinwei=(second->val+jinwei)/10;
third=third->next;
second=second->next;
}
if(jinwei){
third->next=new ListNode(1);
third=third->next;
}
return reverseLink(result);
}
};
题目4(单链表的排序)
收获
1:相较于直接用链表排序的复杂,中间用vector数组辅助排序出的结果,速度比较快,完全自己独立完成
代码
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
class Solution {
public:
/**
*
* @param head ListNode类 the head node
* @return ListNode类
*/
//!!!!!!!aaaaa这是错觉么???好有成就感的感觉!!!!!!!!!!!!本来打算用数组的,但是确实没有
//vector直接排序比较nice!!!
ListNode* sortInList(ListNode* head) {
// write code here
if(head==nullptr||head->next==nullptr)
return head;
ListNode *temp=head;
//创建一个
vector<int >res;
while(temp){
res.push_back(temp->val);
temp=temp->next;
}
sort(res.begin(), res.end());
ListNode *returnnum=new ListNode(res[0]);
temp=returnnum;
for(int i=1;i<res.size();i++){
temp->next=new ListNode(res[i]);
temp=temp->next;
temp->next=NULL;
}
return returnnum;
}
};
题目5(链表的奇偶重排)
收获
1:这道题注意题目要求即可,是节点的编号奇数一边,偶数一边,不要直接对节点数据进行判断了
代码
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) : val(x), next(nullptr) {}
* };
*/
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param head ListNode类
* @return ListNode类
*/
ListNode* oddEvenList(ListNode* head) {
// write code here
if(head==nullptr||head->next==nullptr)
return head;
ListNode *temp=head;
vector<int >jishu;
vector<int >oushu;
int i=0;
while(temp){
i++;
if(i%2==0)
oushu.push_back(temp->val);
else
jishu.push_back(temp->val);
temp=temp->next;
}
ListNode *node=new ListNode(jishu[0]);
temp=node;
for(int j=1;j<jishu.size();j++){
temp->next=new ListNode(jishu[j]);
temp=temp->next;
temp->next=nullptr;
}
for(int j=0;j<oushu.size();j++){
temp->next=new ListNode(oushu[j]);
temp=temp->next;
temp->next=nullptr;
}
return node;
}
};
题目6(删除有序链表中重复的元素)
收获
1:这道题相比于前面的删除掉只剩一个重复的而言,是需要把有重复的就删掉,于是就要记录第一个有重复的位置,一直将指针后移,直到没有重复为止
2:巧用链表之前的pre!
代码
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
class Solution {
public:
/**
*
* @param head ListNode类
* @return ListNode类
*/
ListNode* deleteDuplicates(ListNode* head) {
// write code here
if(head==nullptr||head->next==nullptr)
return head;
ListNode *pre=new ListNode(0);
pre->next=head;
ListNode *cur=pre;
while(cur->next!=NULL&&cur->next->next!=NULL){
if(cur->next->val==cur->next->next->val){
int temp=cur->next->val;
while(cur->next!=NULL&&cur->next->val==temp)
cur->next=cur->next->next;
}
else{
cur=cur->next;
}
}
return pre->next;
}
};
题目7(链表内指定区间反转)
收获
1:这道题真的是本来最开始遇到的就是这题,但是最开始对链表的理解不是很深入,就不是很能理解,后来慢慢的理解了,就是最后的链接怎么将翻转的最后一位,和没翻转的下一位接上的问题,后来意识到原来是cur指针一直不动,利用cur->next,再新建temp进行链接实现的功能(印象很深刻了~)
代码
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
class Solution {
public:
/**
*
* @param head ListNode类
* @param m int整型
* @param n int整型
* @return ListNode类
*/
ListNode* reverseBetween(ListNode* head, int m, int n) {
// write code here
ListNode* res=new ListNode(-1);
res->next=head;
ListNode *pre=res;
//当前节点!!
ListNode *cur=head;
for(int i=1;i<m;i++){
pre=cur;
cur=cur->next;
}
//一直在移动指针,其实真正的操作是用temp和temp->next进行衔接!!!而且cur的位置一直
//保持不变,正好对应的遍历在最后的时候的cur->next进行衔接!!!!呜呜呜
for(int i=m;i<n;i++){
//创建一个新temp等于当前的下一个,用于连接
ListNode * temp=cur->next;
//记录好下一次的位置,方便下一次循环用
cur->next=temp->next;
//建立好连接temp后是pre的下一个
temp->next=pre->next;
//pre的下一个是temp,通过这样就使得pre的next一直向前到翻转链表的尾部·temp之间在一点
//一点的连接
pre->next=temp;
}
return res->next;
}
};
题目8(链表中的节点每 k 个一组翻转)
收获
1:这道题不是自己写的,题解用递归的思想实现,很方便,自己也理解了~
代码
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
class Solution {
public:
/**
*
* @param head ListNode类
* @param k int整型
* @return ListNode类
*/
//唔~理解起来还比较方便!!递归的想法!
ListNode* reverseKGroup(ListNode* head, int k) {
// write code here
ListNode *tail=head;
//遍历k次到尾部,实际上是下一组k的开始!!,注意判断的顺序
for(int i=0;i<k;i++){
if(tail==NULL)
return head;
tail=tail->next;
}
ListNode *pre=NULL;
ListNode *cur=head;
while(cur!=tail){
ListNode *temp=cur->next;
cur->next=pre;
pre=cur;
cur=temp;
}
head->next=reverseKGroup(tail, k);
return pre;
}
};
题目9(合并 k 个已排序的链表)(hard)
收获
1:这道题最开始的思路,时间复杂度大概是k²,而不是题目所要求的log形式,自己想到了可以使用归并排序,但是不是很熟悉,看了题解,觉得自己应该可以写出来,链表和递归的结合的一种~~~
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
// ListNode *sortLink(ListNode *head,ListNode* head2){
// }
// ListNode *mergeKLists(vector<ListNode *> &lists) {
// if(lists.size()==1)
// return lists[0];
// //将对多个进行排序转换为排序n-1次,每次对两个进行升序排序,但是这么做的结果就是
// //时间复杂度不满足要求,最好能同时排序两个,有一种归并排序的思想
// ListNode *head=lists[0];
// for(int i=1;i<lists.size();i++){
// head=sortLink(head,lists[i]);
// }
// return head;
// }
ListNode *sortLink(ListNode *head1,ListNode* head2){
if(head1==NULL)
return head2;
if(head2==NULL)
return head1;
ListNode *head=new ListNode(0);
ListNode *cur=head;
while(head1&&head2){
if(head1->val<head2->val)
{
cur->next=head1;
head1=head1->next;
}
else
{
cur->next=head2;
head2=head2->next;
}
cur=cur->next;
}
if(head1){
cur->next=head1;
}
if(head2){
cur->next=head2;
}
return head->next;
}
//归并排序的思想,一会儿可以练习练习,但是现在要去总结啦~~~
ListNode *mergeLink(vector<ListNode *>&lists,int left,int right){
if(left>right)
return NULL;
else if(left==right)
return lists[left];
int mid=(left+right)/2;
return sortLink(mergeLink(lists, left, mid),mergeLink(lists, mid+1, right));
}
ListNode *mergeKLists(vector<ListNode *> &lists) {
return mergeLink(lists, 0, lists.size()-1);
}
};