代码随想录算法训练营第三天 | 力扣203移除链表元素 力扣206 反转链表 力扣707设计链表

力扣203 .移除链表元素

视频讲解

法一:原链表基础上操作

主要思路:

(1)分类讨论要删除的是头节点与其他节点

易错点:

(1)删头结点时用while,防止出现target = 1,链表是 1 1 1 1 1 1 1 

(2) 要删除链表中一个元素,一定要知道这个节点上一个节点

(3) 删除其他节点时cur -> next = cur -> next -> next

(4) 节点删除后必须释放,释放格式:

 struct ListNode* tmp = cur -> next;
 cur -> next = cur -> next -> next;
 free(tmp);

代码实现:

struct ListNode* removeElements(struct ListNode* head, int val){
    //删除头结点
    while (head != NULL && head -> val == val) {    //删除头结点不用if防止val = 1,而链表是 1 1 1 1 1 1 1
        struct ListNode* tmp = head;
        head = head ->next;
        free(tmp);
    }

    struct ListNode* cur = head;
    while(cur != NULL && cur -> next != NULL) {
        if(cur -> next -> val == val) {     //删的不是cur,是cur的下一位
            struct ListNode* tmp = cur -> next;
            cur -> next = cur -> next -> next;
            free(tmp);
        }
        else {
            cur = cur -> next;
        }
    }
    return head;
}

第一次写错误 

补充

法二:虚拟头结点

主要思路:

在原头结点前插入一个虚拟头结点, 这样就把原式头结点也改为普通节点,修改方式就统一了

易错点:

(1) 遍历链表时头结点是不能改的,必须另外定义一个指针cur来遍历

(2) struct ListNode* cur这样只是声明了一个类型为ListNode的结构体指针,只有写为struct ListNode* dummyHead = (struct ListNode*)malloc(sizeof(struct ListNode))这样才是创造了一个节点

(3) 返回是返回的是dummyHead->next而不是head,因为head可能已被删除

代码实现:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode* dummyHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    dummyHead -> next = head;
    struct ListNode* cur = dummyHead;
    while(cur != NULL && cur -> next != NULL) {
        if(cur -> next -> val == val) {
            struct ListNode* tmp = cur -> next;
            cur -> next = cur -> next -> next;
            free(tmp);
        }
        else {
            cur = cur -> next;
        }
    }
    //通过这种方法释放dummyHead
    struct ListNode* ret = dummyHead -> next;
    free(dummyHead);
    return ret;
}

第一次写错误 

力扣206.反转链表

视频讲解

法一:双指针

主要思路:

定义两个指针cur与pre,用于将链表中指向反转过来

易错点:

(1)cur先初始化为head,pre先初始化为NULL

(2)while控制循环的条件是当cur指向空指针时,这时返回的

代码实现:

struct ListNode* reverseList(struct ListNode* head){
    typedef struct ListNode ListNode;
    ListNode* pre, * cur;
    pre = NULL, cur = head;
    while(cur) {
        ListNode* tmp = cur -> next;
        cur -> next = pre;
        pre = cur;
        cur = tmp;
    }
    return pre;
}

力扣707.设计链表

视频讲解

主要思路:

链表常规操作都在里面了

易错点:

(1)本题的obj就是虚拟头结点

(2)检索下标要注意index=0或超出链表长度,而且由于虚拟头节点的存在,所以cur先从obj->next开始遍历(只要不对链表操作就可以不用头节点)

(3)插入头节点时要注意是插在obj与原头节点之间

(4)注意本题要求在所给index前增加节点,所以cur停在所给index前一个节点

(5)在所给index前增加节点时要注意index<0和超限的情况

(6)增加节点时先new -> cur -> next 再 cur -> next = new

(7)删除所给index对应节点时注意index<0和index超限,其中超限的特殊情况为index恰等于链表,此时cur为尾结点,tmp为NULL,不能free,而cur -> next -> next不存在

代码实现:

//本题意思是链表里已经有虚拟头结点了
typedef struct MyLinkedList{
    int val;
    struct MyLinkedList* next;
} MyLinkedList;

MyLinkedList* myLinkedListCreate() {
    MyLinkedList* new = (MyLinkedList* )malloc(sizeof(MyLinkedList));
    new -> next = NULL;
    return new;    
}
//检索下标
int myLinkedListGet(MyLinkedList* obj, int index) {
    int ret = -1;
    MyLinkedList* cur = obj -> next;    //因为obj是虚拟头结点
    for(int i = 0; cur != NULL; i++) {
        if(i == index) {
            ret = cur -> val;
        }
        else {
            cur = cur -> next;
        }
    }
    return ret;
}
//增加头结点
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
    MyLinkedList* dummyHead = (MyLinkedList* )malloc(sizeof(MyLinkedList));
    dummyHead -> val = val;
    dummyHead -> next = obj -> next;
    obj -> next = dummyHead; 
}
//增加尾结点
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
    MyLinkedList* newTail = (MyLinkedList* )malloc(sizeof(MyLinkedList));
    MyLinkedList* cur = obj;
    while(cur -> next != NULL) {    //尾结点插入时必须先遍历
        cur = cur -> next;
    }
    newTail -> val = val;
    cur -> next = newTail;
    newTail -> next = NULL;    //还要记得把新的尾结点的next赋值NULL
}
//在所给index前增加节点
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
    if(index < 0) {
        myLinkedListAddAtHead(obj, val);
        return;
    }
    MyLinkedList* cur = obj;
    for(int i = 0; cur != NULL; i++) {
        if(i == index) {
            MyLinkedList* new = (MyLinkedList* )malloc(sizeof(MyLinkedList));
            new -> val = val;
            new -> next = cur -> next;
            cur -> next = new;
            return;
        }
        else {
            cur = cur -> next;
        }
    }
}
//删除对应索引的节点
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
    if(index < 0) {
        return;
    }    
    MyLinkedList* cur = obj;
    for(int i = 0; cur != NULL; i++) {
        if(i == index) {
            MyLinkedList* tmp = cur -> next;
            if(tmp != NULL) {
                cur -> next = cur -> next -> next;
                free(tmp);
            }
        }
        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);
*/

总结:

修改链表是可以考虑虚拟头节点,有虚拟头节点是cur一般停在目标index前一个节点

你可能感兴趣的:(算法训练营,算法,链表,数据结构)