目录
206.反转链表(链表逆序)(easy)
要求:不可以额外申请空间
链表翻转操作的顺序对于迭代来说是从链头往链尾,而对于递归是从链尾往链头。
思考:如果要反转链表,比如[1->2->3]这么一个链表,操作就是把1->2 给变成 2->1就可以了,但是这样就出现个问题,2->1 ,那么怎么再通过这个链表去3呢?因为2->3这个链已经断开了,所以这个问题有个关键操作就是“把当前结点的next用一个指针给保存起来”,这样改变了链接方向也不怕了,再把下一个指针赋值给head就可以了。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(!head) return NULL;
ListNode* new_head = NULL;
while(head){
ListNode *p = head->next; //首先要保存当前结点的下一个结点的地址
head->next = new_head;//把当前的结点反逆转
new_head = head ;//新链表的首部已经变化了
head = p;//当前结点再前往下一个结点
}
return new_head;
}
};
递归:首先指针H迭代到底如下图所示,并且设置一个新的指针作为翻转后的链表的头。由于整个链表翻转之后的头就是最后一个数,所以整个过程NewH指针一直指向存放5的地址空间。
然后H指针逐层返回的时候依次做下图的处理,将H指向的地址赋值给H->next->next指针,并且一定要记得让H->next =NULL,也就是断开现在指针的链接,否则新的链表形成了环,下一层H->next->next赋值的时候会覆盖后续的值。
递归的解法转载了(https://blog.csdn.net/fx677588/article/details/72357389)文章中的部分。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head)
{
if(head == NULL||head->next ==NULL)
return head;
ListNode* newhead = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return newhead;
}
};
92.反转链表II(medium)
要求:不可以额外生成空间
思路:和第一个反转链表一样,但是这里有四个关键的结点要注意,1、反转链表(m,n)的前一个结点 2、位置m的节点 3 位置n的节点 4 位置n+1 的结点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
if(!head) return NULL;
ListNode* ori_head = head;
ListNode* preHead =NULL;
int change_len = n-m+1;
while(head&&--m){
preHead = head;
head = head->next;
}
ListNode* reverseTail = head;
ListNode* newHead = NULL;
while(head&& change_len){
ListNode* p =head->next;
head->next = newHead;
newHead = head;
head = p;
change_len--;
}
ListNode* reverseHead = newHead ;
ListNode* lastTail = head;
reverseTail->next = lastTail;
if(preHead) //如果不是从头开始逆转,prehead = NULL
{
preHead->next = reverseHead;
}
else{
ori_head = reverseHead;
}
return ori_head;
}
};
160.相交链表(easy)
思考:
方法一:采用 set,存储下来linklistA的各个结点的地址,然后对LinkList的每个结点地址进行查询,如果查的到,说明两个节点有相交;否则没有相交。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
set<ListNode *> address_set;
while(headA){
address_set.insert(headA);
headA = headA->next;
}
while(headB){
if(address_set.find(headB)!=address_set.end()){
return headB;
}
headB = headB->next;
}
return NULL;
}
};
方法二:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
int getListLength(ListNode *headA){
ListNode *p = headA;int lengthA = 0;
while(p!=NULL){lengthA++;
p=p->next;
}
return lengthA;
}
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(headA == NULL||headB == NULL)
return NULL;
int lengthA = getListLength(headA);
int lengthB = getListLength(headB);
ListNode * p = headA;
ListNode * q = headB;
int step = lengthA - lengthB;
if(step==0){
while(p!=q)
{
if(p==NULL)
return NULL;
else{
p=p->next;
q=q->next;
}
}
return p;
}
else{
if(step >0){
for(int i =1 ;i<=step;i++)
{
p = p->next;
}
while(p!=q)
{
if(p==NULL)
return NULL;
else{
p=p->next;
q=q->next;
}
}
return p;
}
else{
for(int i =step ;i<0;i++)
{
q = q->next;
}
while(p!=q)
{
if(p==NULL)
return NULL;
else{
p=p->next;
q=q->next;
}
}
return p;}
}
}
};
142.环形链表II(medium)
思考:这个题可以设置快慢指针,fast每次走两步,slow每次走一步,如果有环两者一定会相遇。(就像我们在操场跑步,跑得快的同学肯定还会在追上那个跑的慢的同学)。
在141.环形链表I有个问题我每次容易犯错误,就是如果没有环,那fast越跑越远和slow不是永远不会相交了?
答:首先判断while(head&&head->next ),只要两者为空就停止循环,这就说明了链表没有环;其次再判断slow 和fast相等吗?相等就说明存在环。
附141.环形链表I代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head == NULL||head->next == NULL){
return false;
}
ListNode *slow = head;
ListNode *fast = head;
while(fast&&fast->next){
slow = slow->next;
fast = fast->next->next;
if(slow == fast ){
return true;
}
}
return false;
}
};
思路一:利用set来判断,把链表插入进set,当第一个重复出现的结点就是需要查找的。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
set<ListNode*> address_set;
while(head){
if(address_set.find(head)!= address_set.end()){
return head;
}
address_set.insert(head);
head = head->next;
}
return NULL;
}
};
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *fast = head;
ListNode *slow = head;
ListNode *meet = NULL;
if(head==NULL||head->next==NULL){
return NULL;
}
while(fast&&fast->next){
fast = fast->next->next;
slow = slow->next;
if(slow == fast){
meet = fast;
break;
}
}
if(meet == NULL){
return NULL;
}
while(head != meet){
head = head->next;
meet = meet->next;
}
return head;
}
};
86.分割链表(medium)
思路:创建两个临时结点,要注意的一点,临时结点是结构体,如果要查找下一个结点的地址是用'.'而不是'->'
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
//创建头结点而不是头结点的指针
//ListNode* less_head = new ListNode(0);
//ListNode* more_head = new ListNode(0);
ListNode less_head = ListNode(0);
ListNode more_head = ListNode(0);
ListNode* p = &less_head;
ListNode* q = &more_head;
while(head){
if(head->val < x){
p->next = head;
//head = head->next;
//两个操作都需要head指针向后移动 所以可以写在if else外面
p = head;
}
else{
q->next = head;
q = head;
}
head = head->next;
}
//p->next = &more_head; 是和more_head 的下一个结点的地址相链接
p->next = more_head.next;
q->next = NULL;
//return less_head->next; 结点要以结构体的形式去访问
return less_head.next;
}
};
138.赋值带随机指针的链表(复杂链表的复制)(hard)
思考:
深度拷贝:生成一个全新的链表和原来的链表毫无关系(改变旧的不影响新的,改变新的不影响旧的)。
难点:比如第一个节点随机指向第三个节点的地址,但是重新创造一个全新链表的时候,你的地址已经变了,靠地址信息无法再找到对应的结点了。所以应该想到MAP映射,把地址和第几个节点映射起来,只要存在结点之间的对应关系,就不怕找不到了。
把链表A的所有地址标号1,2,...。建立 (地址->编号)的映射。
所以a->random也是地址,直接可以找到它对应哪个结点。
map<type1,type2> --->建立type1和type2的映射。
/**
* Definition for singly-linked list with a random pointer.
* struct RandomListNode {
* int label;
* RandomListNode *next, *random;
* RandomListNode(int x) : label(x), next(NULL), random(NULL) {}
* };
*/
class Solution {
public:
RandomListNode *copyRandomList(RandomListNode *head) {
map<RandomListNode *,int> node_map;
vector<RandomListNode*> node_vec;
int i = 0;
RandomListNode* ptr = head;
while(ptr){
node_vec.push_back(new RandomListNode(ptr->label));
node_map[ptr] = i;
ptr = ptr->next;
i++;
}
node_vec.push_back(NULL);
ptr = head; i = 0;
while(ptr){
node_vec[i]->next = node_vec[i+1];
if(ptr->random){
int corresponse_node = node_map[ptr->random];
node_vec[i]->random = node_vec[corresponse_node];
}
ptr = ptr->next;
i++;
}
return node_vec[0];
}
};
21.合并两个有序链表(easy)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode l(0);
ListNode* ptr = &l;
while(l1&&l2){
if(l1->val <= l2->val){
ptr->next= l1;
// ptr = l1;
l1 = l1->next;
}
else{
ptr->next = l2;
//ptr = l2;
l2 = l2->next;
}
ptr =ptr->next; //合成新链表,无论是list1 还是list2 的结点 新链表的当前结点指针都要向后指一位
}
if(l1){
ptr->next = l1;
}
if(l2){
ptr->next = l2;
}
return l.next;
}
};
23.合并K个排序链表(hard)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode l(0);
ListNode* ptr = &l;
while(l1&&l2){
if(l1->val <= l2->val){
ptr->next= l1;
// ptr = l1;
l1 = l1->next;
}
else{
ptr->next = l2;
//ptr = l2;
l2 = l2->next;
}
ptr =ptr->next;
}
if(l1){
ptr->next = l1;
}
if(l2){
ptr->next = l2;
}
return l.next;
}
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if(lists.empty()){
return NULL;
}
if(lists.size()==1){
return lists[0];
}
if(lists.size()==2){
return mergeTwoLists(lists[0],lists[1]);
}
int mid = lists.size()/2;
vector<ListNode*> sub_list1;
vector<ListNode*> sub_list2;
for(int i = 0;i<mid;i++){
sub_list1.push_back(lists[i]);
}
for(int i = mid;i<lists.size();i++){
sub_list2.push_back(lists[i]);
}
ListNode* l1 = mergeKLists(sub_list1);
ListNode* l2 = mergeKLists(sub_list2);
return mergeTwoLists(l1,l2);
}
};