理论基础
种类:单链表,双链表(有前节点也有后节点,即可向前查询也可向后查询),循环链表
存储方式:和一维数组不一样,链表节点在内存中不是连续分布的。
定义链表构造函数:C++如果没定义直接使用也行因为系统有默认链表构造函数,但无法在初始化的时候赋值而需要初始化后附值
增删查改:数组查改快增删慢适用于查改多增删少的情景,链表正好相反。
203移除链表元素
203. Remove Linked List Elements
2设置虚拟头节点进行操作
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val) {
//设置虚拟头节点进行移除节点操作
//记得虚拟头节点要分配内存
//给struct ListNode数据类型用typedef起别名ListNode。
typedef struct ListNode ListNode;
ListNode* shead;
shead=(ListNode*)malloc(sizeof(ListNode));
shead->next=head;
ListNode* cur=shead;
while(cur->next){
if(cur->next->val==val){
ListNode* temp=cur->next;
cur->next=temp->next;
free(temp);
}else{
cur=cur->next;
}
}
head=shead->next;
return head;
}
1使用原链表进行操作
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val) {
//使用原链表进行移除节点操作
struct ListNode* temp;
//先分开处理头节点
//对节点进行操作前先要确认其存在
//即不等于NULL,不能直接写null会被视为未定义,也可以直接用节点本身作为判断依据即可
while(head && head->val == val){
temp=head;
head=head->next;
free(temp);
}
//再处理非头节点
//cur相当于next指针,我们把它叫做游标。
//确保head不是因为删完了等于null才跳出的,否则cur=head为null时不能进行操作
struct ListNode* cur;
cur=head;
while(cur&&(temp=cur->next)){
if(temp->val==val){
cur->next=temp->next;
free(temp);
}else{
cur=cur->next;
}
}
return head;
}
707设计链表
注意:
1.该题默认obj指的是虚拟头节点,但index为0时指的是非虚拟头节点
2.不可以直接用cur->next->next
3.DeleteAtIndex中cur->next!=NULL也需要判断,否则会漏index为(末尾下标+1)时,cur为末尾元素时的情况(cur!=NULL但cur->next==NULL)。上面这点在AddAtIndex中不需要考虑因为题目要求说如果 index 等于链表的长度(末尾下标+1),那么该节点会被追加到链表的末尾。
//该题默认obj指的是虚拟头节点,但index为0时指的是非虚拟头节点
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;
//要判断一下index是否超出链表长度或者小于0
//则需要判断遍历完链表即指针地址为NULL时查看是否能找到index对应下标的元素
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* nhead=(MyLinkedList*)malloc(sizeof(MyLinkedList));
nhead->val=val;
nhead->next=obj->next;
obj->next=nhead;
}
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
MyLinkedList* ntail=(MyLinkedList*)malloc(sizeof(MyLinkedList));
ntail->val=val;
ntail->next=NULL;
MyLinkedList* cur=obj;
while(cur->next!=NULL){
cur=cur->next;
}
cur->next=ntail;
}
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
MyLinkedList* newnode=(MyLinkedList*)malloc(sizeof(MyLinkedList));
newnode->val=val;
MyLinkedList* cur=obj;
//要判断一下index是否超出链表长度或者小于0
//则需要判断遍历完链表即指针地址为NULL时查看是否能找到index对应下标的元素
//注意在DeleteAtIndex中cur->next!=NULL也需要判断,否则会漏index为(末尾下标+1)时,cur为末尾元素时的情况(cur!=NULL但cur->next==NULL)
//上面这点在AddAtIndex中不需要考虑因为题目要求说如果 index 等于链表的长度(末尾下标+1),那么该节点会被追加到链表的末尾。
for(int i=-1;cur!=NULL;i++){
if(i==index-1){
newnode->next=cur->next;
cur->next=newnode;
return;
}else{
cur=cur->next;
}
}
return;
}
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
MyLinkedList* cur=obj;
//要判断一下index是否超出链表长度或者小于0
//则需要判断遍历完链表即指针地址为NULL时查看是否能找到index对应下标的元素
//注意在DeleteAtIndex中cur->next!=NULL也需要判断,否则会漏index为(末尾下标+1)时,cur为末尾元素时的情况(cur!=NULL但cur->next==NULL)
//上面这点在AddAtIndex中不需要考虑因为题目要求说如果 index 等于链表的长度(末尾下标+1),那么该节点会被追加到链表的末尾。
for(int i=-1;cur!=NULL&&cur->next!=NULL;i++){
if(i==index-1){
//注意不可以直接用cur->next->next
MyLinkedList* temp=cur->next;
cur->next=temp->next;
free(temp);
}else{
cur=cur->next;
}
}
return;
}
void myLinkedListFree(MyLinkedList* obj) {
//去掉虚拟头节点
while(obj!=NULL){
MyLinkedList* temp=obj;
obj=obj->next;
free(temp);
}
}
/**
* 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反转链表
递归法
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
//递归法
struct ListNode* reverse(struct ListNode* pre,struct ListNode* cur){
if(cur==NULL){
return pre;
}else{
struct ListNode* temp=cur->next;
cur->next=pre;
// 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步
// pre = cur;
// cur = temp;
return reverse(cur,temp);
}
}
struct ListNode* reverseList(struct ListNode* head) {
//用pre和cur做定位(cur为需改变指向元素,pre为cur前一位,提供cur的next指向,temp记录pre下一位的next指向)
//不断后移逐个改变链表元素next的指向
//即,将cur的next指向自己的前一位元素pre,再后移pre和cur,直到cur为null即不再有元素需要改变指向。
//cur记录pre下一位的地址(但不能直接用pre->next因为已被改变指向),
//最后把cur的next指向pre,pre后移则直接赋值为cur即可,cur后移则赋值为temp
return reverse(NULL,head);
}
双指针法
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
//双指针法
struct ListNode* reverseList(struct ListNode* head) {
//用pre和cur做定位(cur为需改变指向元素,pre为cur前一位,提供cur的next指向,temp记录pre下一位的next指向)
//不断后移逐个改变链表元素next的指向
//即,将cur的next指向自己的前一位元素pre,再后移pre和cur,直到cur为null即不再有元素需要改变指向。
//cur记录pre下一位的地址(但不能直接用pre->next因为已被改变指向),
//最后把cur的next指向pre,pre后移则直接赋值为cur即可,cur后移则赋值为temp
typedef struct ListNode ListNode;
ListNode* pre=NULL;
ListNode* cur=head;
ListNode* temp;
while(cur!=NULL){
temp=cur->next;
cur->next=pre;
pre=cur;
cur=temp;
}
return pre;
}