代码随想录算法训练营day2之链表

链表定义

做题前复习链表定义:

// 单链表
struct ListNode {
    int val;  // 节点上存储的元素
    ListNode *next;  // 指向下一个节点的指针
    ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数
};

203、移除链表元素

题目链接:

203、移除链表元素

题目描述:

给出头结点和val,删除链表中值为val的元素,最后返回head

做题状态:

链表是半个月前学完的,这种简单题居然不能立马做出来,o(╥﹏╥)o,好吧,有些东西还是及时复习,多敲几遍才行啊。迭代差不多还能敲出来,递归可以二刷复习一下。

题解:

1.迭代:

1、如何删除:找到要删除的结点的前一个结点即可,code : pre->next=pre->next->next;

2、先要删除前面的所有要删除的,如:[ 7 , 7 ,7 ,9, 5] , val=7, 也就是说把头结点 head 指向第一共不删除的结点

3、一个家常便饭:如果 head 为空,直接返回

code:

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        while (head && head->val == val) {
            head = head->next;
        }

        if (!head) {
            return head;
        }

        ListNode* pre = head;
        while (pre->next) {
            if (pre->next->val == val) {
                pre->next = pre->next->next;
            } else {
                pre = pre->next;
            }
        }
        return head;
    }
};

2.递归:

每次做递归都要想好一会儿,可能我太若基了……

主要思路我理解的是从前往后压,就是进入递归,从后往前弹,一层一层出递归。

如果为val,返回next,否则返回自己。

code(官方):

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        if (head == nullptr) {
            return head;
        }
        head->next = removeElements(head->next, val);
        return head->val == val ? head->next : head;
    }
};

707设计链表

题目链接:

707、设计链表

题目描述:

就是要自己写链表初始化、int get(int index)、void addAtHead(int val)、void addAtTail(int val) 、void addAtIndex(int index, int val)、void deleteAtIndex(int index)然后封装。

做题状态:

这些我在学链表的时候自己还专门默写了几遍,果然,功夫不负有心人,不会写了hhhhh,诶。果然要及时复习啊啊啊啊。

题解:

因为下学期要学数据结构,基本是c语言,我就找了c语言的题解

重点:

1.下标访问不合理

2.内存的存储和释放 malloc 和 free,c++是 new 和 delete

3.首尾结点的处理

4.避免空指针

5.忘记写size了  o(╥﹏╥)o

code

来源:作者:阿松啊   链接:点此查看

我觉得这位佬写的特别详细,不清楚就看看链接




typedef struct MyLinkedList_t{
	int val;
	struct MyLinkedList_t *next;
} MyLinkedList;

/** Initialize your data structure here. */

MyLinkedList* myLinkedListCreate() {
    MyLinkedList *obj = (MyLinkedList *)malloc(sizeof(MyLinkedList));
	obj->val = 0;
	obj->next = NULL;
	return obj;
}

/** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
int myLinkedListGet(MyLinkedList* obj, int index) {
	if (index < 0 || obj->next == NULL) {
		return -1;
	}

	int now = 0;
	MyLinkedList *listNow = obj->next;
	while (now < index) {
		if (listNow == NULL) {
			return -1;
		}

		listNow = listNow->next;
		now++;
	}

	if (listNow != NULL) {
		return listNow->val;
	}

	return -1;
}

/** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
	MyLinkedList *Node = (MyLinkedList *)malloc(sizeof(MyLinkedList));
	Node->val = val;
	Node->next = NULL;

	if (obj->next == NULL) {
		obj->next = Node;
		return;
	} else {
		Node->next = obj->next;
		obj->next = Node;
	}
}

/** Append a node of value val to the last element of the linked list. */
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
	MyLinkedList *Node = (MyLinkedList *)malloc(sizeof(MyLinkedList));
	Node->val = val;
	Node->next = NULL;

	MyLinkedList *nowList = obj;
	while (nowList->next != NULL) {
		nowList = nowList->next;
	}

	nowList->next = Node;
}

/** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
	if (index <= 0) {
		myLinkedListAddAtHead(obj, val);
	}

	int now = 0;
	MyLinkedList *nowList = obj->next;
	while (nowList->next != NULL) {
		if (now == index - 1) {
			break;
		}
		nowList = nowList->next;
		now++;
	}

	if (index - 1 != now) {
		return;
	}

	MyLinkedList *Node = (MyLinkedList *)malloc(sizeof(MyLinkedList));
	Node->val = val;
	Node->next = nowList->next;
	nowList->next = Node;
}

/** Delete the index-th node in the linked list, if the index is valid. */
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
	if (index < 0 || obj->next == NULL) {
		return;
	}

	if (index == 0) {
		obj->next = obj->next->next;
		return;
	}

	MyLinkedList *nowList = obj->next;
	int now = 0;
	while (nowList->next != NULL) {
		if (now == index - 1) {
			break;
		}
		nowList = nowList->next;
		now++;
	}

	if (now == index - 1 && nowList->next != NULL) {
		nowList->next = nowList->next->next;
	}
}

void myNodeFree(MyLinkedList* Node) {
	if (Node->next != NULL) {
		myNodeFree(Node->next);
		Node->next = NULL;
	}
	free(Node);
	
}

void myLinkedListFree(MyLinkedList* obj) {
    myNodeFree(obj);
}

/**
 * Your MyLinkedList struct will be instantiated and called as such:
 * MyLinkedList* obj = myLinkedListCreate();
 * int param_1 = myLinkedListGet(obj, index);
 
 * myLinkedListAddAtHead(obj, val);
 
 * myLinkedListAddAtTail(obj, val);
 
 * myLinkedListAddAtIndex(obj, index, val);
 
 * myLinkedListDeleteAtIndex(obj, index);
 
 * myLinkedListFree(obj);
*/

206、反转链表

题目链接:

206反转链表

题目描述:

反转一个链表

做题状态:

以前做过这道题,迭代记得怎么做,挺简单的,但是忘记递归怎么做了

题解:

1.迭代

只要注意用next存放cur指针的下一个结点就行

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode *pre=NULL;
    struct ListNode* cur=head;
    while(cur){
        struct ListNode* next=cur->next;
        cur->next=pre;
        pre=cur;cur=next;
    }
    return pre;
    
}

2.递归

code(官方)

重点:head->next->next=head,就是当前结点的下一个结点要指向自己

注意:结点为空和下一个结点为空要单独判断

struct ListNode* reverseList(struct ListNode* head) {
    if (head == NULL || head->next == NULL) {
        return head;
    }
    struct ListNode* newHead = reverseList(head->next);
    head->next->next = head;
    head->next = NULL;
    return newHead;
}

24两两交换链表中的结点

题目链接:

题目描述:

交换相邻结点,但不能只改变值

做题状态:

半会不会的,可能这就是让我进步的题目吧……大概思路挺懂的,交换的代码也会,但是理不清晰

题解:

1.迭代

关键在于找谁为while的条件,此题用temp,要交换的结点的前一个结点(嘻嘻,妙),要考虑奇数个结点的情况


struct ListNode* swapPairs(struct ListNode* head) {
    struct ListNode dummyHead;
    dummyHead.next = head;
    struct ListNode* temp = &dummyHead;
    while (temp->next != NULL && temp->next->next != NULL) {
        struct ListNode* node1 = temp->next;
        struct ListNode* node2 = temp->next->next;
        temp->next = node2;
        node1->next = node2->next;
        node2->next = node1;
        temp = node1;
    }
    return dummyHead.next;
}

2.递归

这次递归倒是想得挺明白的:newhead为当前第二个,之后的第一个结点,head为当前第一个结点,之后的第二个结点,所以,如果后面的都交换完了,head->next为后面交换完的链表的第一个结点,即swapPairs(newhead->next),head处理完毕,再将newhead指向head.

在想递归的时候,考虑假设(假设递归完了)或者栈(先进后出,一层一层)的思想,不要钻牛角尖。

struct ListNode* swapPairs(struct ListNode* head) {
    if (head == NULL || head->next == NULL) {
        return head;
    }
    struct ListNode* newHead = head->next;
    head->next = swapPairs(newHead->next);
    newHead->next = head;
    return newHead;
}

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

题目链接:

点这里

题意:

如题

做题状态:

想到了第一种方法,基本实现了,但后面两种也太棒了

题解

1.计算长度L

先计算出长度再往后依次找出倒数第n-1个结点,最后删除,下面官方代码:

int getLength(struct ListNode* head) {
    int length = 0;
    while (head) {
        ++length;
        head = head->next;
    }
    return length;
}

struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
    struct ListNode* dummy = malloc(sizeof(struct ListNode));
    dummy->val = 0, dummy->next = head;
    int length = getLength(head);
    struct ListNode* cur = dummy;
    for (int i = 1; i < length - n + 1; ++i) {
        cur = cur->next;
    }
    cur->next = cur->next->next;
    struct ListNode* ans = dummy->next;
    free(dummy);
    return ans;
}

2.利用栈

因为栈先进后出,那把链表从头到尾放进去,再取出倒数第n个,删除即可

官方题解:

class Solution {
public:
    ListNode* removeNthFromEnd(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) {
            stk.pop();
        }
        ListNode* prev = stk.top();
        prev->next = prev->next->next;
        ListNode* ans = dummy->next;
        delete dummy;
        return ans;
    }
};

3.利用快慢指针

作者:Smile
来自佬的一篇题解,太强了。

快慢指针的题其实做过不少,这里没想起来,让快指针先走n+1步,然后一起走,直到快的到空,此时慢指针的下一个结点为要删除的结点

struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
   
    struct ListNode* dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
    dummy->next = head;
    struct ListNode* front = dummy;
    struct ListNode* rear = dummy;
    for (int i = 0; i <= n; i++) {
        front = front->next;
    }
    while (front != NULL) {
        front = front->next;
        rear = rear->next;
    }
    struct ListNode* toDelete = rear->next;
    rear->next = rear->next->next;
    free(toDelete);
    struct ListNode* newHead = dummy->next;
    free(dummy);
    return newHead;
}

链表相交

题目链接:

点这里

题意:

找到连个链表相交的结点并返回,不相交返回null.

做题状态:

以前做过,哈希秒了,不过忘记快慢指针的方法了

题解:

1.快慢指针

若两个链表长度分别为m、n,两个指针从甲乙头结点开始,让甲指针后接上乙,乙后面接上甲,那么两个指针必然同时指向那个相交的结点,或者指向null,也就是说,自己的结点遍历完了就开始遍历另一个链表的结点了。泰裤辣!!!

评论区一句话:“原来数学才是真正的优雅”,狠狠赞了o( ̄▽ ̄)d

有篇题解名:“只要以相同速度前进,总能遇见你”,好有意思,写下来记录一下(*^▽^*)

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if (headA == NULL || headB == NULL) {
        return NULL;
    }
    struct ListNode *pA = headA, *pB = headB;
    while (pA != pB) {
        pA = pA == NULL ? headB : pA->next;
        pB = pB == NULL ? headA : pB->next;
    }
    return pA;
}

2.哈希

有点简单,二刷不会看看链接吧

3.快慢指针

跟方法一是不同的,这里先求出m、n,然后让长的链表的指针先走几步,并列后再一起走,over

总结:

呀呼!!!今天做了六道题!但是明显没有昨天那么轻松了,因为链表的知识忘了不少,刷的题也大大的不够,不过还是很开心,继续加油!

1.哑指针的使用

2.内存的释放和分配

3.递归、双指针、栈在链表中的使用

4.链表的基础定义

明日任务:

1.重写设计链表,温故知新

2.复习8.2所做题目,再拓展至少两道数组题

3.代码随想录day03任务

4.复习链表里有关递归的题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

失序空间

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值