前言
打卡第三天,加油!
链表知识
【链表 linked list】是一种线性数据结构,其中的每个元素都是一个节点对象,链表是一种通过指针串联在一起的,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。链表的入口节点称为链表的头结点也就是head。
各个节点通过“引用”相连接。引用记录了下一个节点的内存地址,通过它可以从当前节点访问到下一个节点。链表的设计使得各个节点可以分散存储在内存各处,它们的内存地址无须连续。
单链表
双链表
单链表中的指针域只能指向节点的下一个节点。
双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。
双链表 既可以向前查询也可以向后查询。
循环链表
循环链表,顾名思义,就是链表首尾相连。循环链表可以用来解决约瑟夫环问题。
代码实现
链表结点的定义
C
//结构体定义
typedef struct ListNode{
int element;
struct ListNode *next;}ListNode;
Python
class ListNode:
def __init__(self,int val):
self.val = val
self.next = None
移除链表元素
203. 移除链表元素https://leetcode.cn/problems/remove-linked-list-elements/
删除值与target相等的结点有两种情况
1,不设置新的头结点,直接使用原来的链表来进行移除。
删除结点的时候需要找到被删除结点的上一个结点,但是头结点没有,所以需要为删除头结点单独写一段代码
当头结点可以保留的时候,可以开始比较头结点后的结点,因为返回链表需要返回头结点,所以在遍历链表的时候不能通过头结点来遍历,需要再新建一个指针来进行链表的遍历和结点的删除。
2,创建一个新的头结点
当创建一个新结点后,该结点指向头结点,这样可以使链表的所有结点删除操作变的相同
但是结点的遍历任然需要新设置一个遍历指针来实现,理由同上,头指针需要被用来返回链表时候使用
C语言 【原链表版】
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val) {
struct ListNode *temp;
while(head&&head->val==val){
temp = head;
head = head->next;
free(temp);
}
struct ListNode *cur = head;
while(cur&&(temp=cur->next)){
if(temp->val == val){
cur ->next = cur->next->next;
free(temp);
}
else{
cur= cur->next;
}
}
return head;
}
C语言 【设置头指针版】
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val) {
typedef struct ListNode ListNode;
ListNode *newNode;
newNode = (ListNode *)malloc(sizeof(ListNode));
newNode->next = head;
ListNode *cur = newNode;
while(cur->next != NULL){
if(cur->next->val == val){
ListNode *temp;
temp = cur->next;
cur->next = cur->next->next;
free(temp);
}
else{
cur = cur -> next;
}
}
head = newNode->next;
free(newNode);
return head;
}
设计链表
707. 设计链表https://leetcode.cn/problems/design-linked-list/
typedef struct {
int val;
struct MyLinkedList* next;
}MyLinkedList;
//这个题必须用虚拟头指针,参数都是一级指针,头节点确定后没法改指向了!!!
MyLinkedList* myLinkedListCreate() {
MyLinkedList *head = (MyLinkedList*)malloc(sizeof(MyLinkedList));
head ->next = NULL;
return head;
}
int myLinkedListGet(MyLinkedList* obj, int index) {
MyLinkedList* cur = obj->next;
for(int i=0;cur!=NULL;i++){
if(i == index){
return cur->val;
}
else{
cur = cur->next;
}
}
return -1;
}
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
MyLinkedList *new = (MyLinkedList*)malloc(sizeof(MyLinkedList));
new->val = val;
new->next = obj -> next;
obj->next = new;
}
//单链表实现尾插法必须先新建一个指针,遍历到链表尾部,再进行插入
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
MyLinkedList *cur = obj; //注意下指针为什么是从虚拟头指针开始
while(cur->next != NULL){
cur = cur ->next;
}
MyLinkedList *new = (MyLinkedList*)malloc(sizeof(MyLinkedList));
new->val = val;
cur->next = new;
new->next = NULL;
}
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
if(index ==0){
myLinkedListAddAtHead(obj,val);
return;
}
MyLinkedList *new = (MyLinkedList*)malloc(sizeof(MyLinkedList));
new->val = val;
MyLinkedList *cur = obj->next;
for(int i= 1; cur!=NULL;i++){
if(i == index ){
new->val = val;
new->next = cur->next;
cur->next = new;
}
else{
cur = cur->next;
}
}
}
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
if(index == 0){
MyLinkedList *cur0 = obj-> next;
if(cur0 != NULL){
obj->next = cur0 ->next;
free(cur0);
}
}
MyLinkedList *cur = obj-> next;
for(int i=1;cur!=NULL&& cur->next != NULL;i++){
if(i == index){
MyLinkedList*dnode = cur->next;
if(dnode != NULL){
cur ->next = dnode->next;
free(dnode);
}
return;
}
else{
cur = cur->next;
}
}
}
void myLinkedListFree(MyLinkedList* obj) {
while(obj != NULL){
MyLinkedList *tmp = obj;
obj = obj->next;
free(tmp);
}
}
/**
* 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. 反转链表https://leetcode.cn/problems/reverse-linked-list/
首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。
然后就要开始反转了,首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下这个节点。
为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。
接下来,就是循环走如下代码逻辑了,继续移动pre和cur指针。
最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。 此时我们return pre指针就可以了,pre指针就指向了新的头结点。
#双指针法
/**
* 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;
while(head){
cur = head->next;
head->next = pre;
pre = head;
head = cur;
}
return pre;
}
这里在定义cur指针不能直接赋值是因为cur的目的是保存下一个待翻转的结点的地址,该地址在循环中是不断变化的,即使是赋值也会被覆盖