707. 设计链表(采用虚拟头节点,神中神)

44 阅读4分钟

707. 设计链表

已解答

中等

相关标签

premium lock icon相关企业

你可以选择使用单链表或者双链表,设计并实现自己的链表。

单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。

如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。

实现 MyLinkedList 类:

  • MyLinkedList() 初始化 MyLinkedList 对象。
  • int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
  • void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
  • void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
  • void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
  • void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。

 

示例:

输入
["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"]
[[], [1], [3], [1, 2], [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]

解释
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2);    // 链表变为 1->2->3
myLinkedList.get(1);              // 返回 2
myLinkedList.deleteAtIndex(1);    // 现在,链表变为 1->3
myLinkedList.get(1);              // 返回 3

 

提示:

  • 0 <= index, val <= 1000
  • 请不要使用内置的 LinkedList 库。
  • 调用 getaddAtHeadaddAtTailaddAtIndex 和 deleteAtIndex 的次数不超过 2000 。

题解:

        
    typedef struct Node {
        int val;
        struct Node* next;
    } Node;

    typedef struct {
        int size;
        Node* next;
    } MyLinkedList;


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

    int myLinkedListGet(MyLinkedList* obj, int index) {
        if (obj->size <= index)
        {
            return -1;
        }

        int i;
        Node* cur;
        cur = obj->next;
        for (i = 0;i < index;i ++)
        {
            cur = cur->next;
        }
        return cur->val;
    }

    void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
        Node* new = (Node*)malloc(sizeof(Node));
        new->next = obj->next;
        new->val = val;
        obj->next = new;
        obj->size ++;
        return;
    }

    void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
        if (obj->size == 0)
        {
            Node* new = (Node*)malloc(sizeof(Node));
            new->next = NULL;
            new->val = val;
            obj->next = new;
            obj->size ++;
            return;
        }

        Node* pre = obj->next;
        int i;
        for (i = 1;i < obj->size;i ++)
        {
            pre = pre->next;
        }
        Node* new = (Node*)malloc(sizeof(Node));
        new->next = NULL;
        new->val = val;
        pre->next = new;
        obj->size ++;
        return;
    }

    void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
        if (obj->size < index)
        {
            return;
        }

        if (index == 0)
        {
            Node* new = (Node*)malloc(sizeof(Node));
            new->next = obj->next;
            new->val = val;
            obj->next = new;
            obj->size ++;
            return;
        }
        Node* pre = obj->next;
        int i;
        for (i = 1;i < index;i ++)
        {
            pre = pre->next;
        }
        Node* new = (Node*)malloc(sizeof(Node));
        new->next = pre->next;
        new->val = val;
        pre->next = new;
        obj->size ++;
        return;
    }

    void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
        if (obj->size <= index)
        {
            return;
        }

        if (index == 0)
        {
            Node* cur = obj->next;
            obj->next = cur->next;
            free(cur);
            cur = NULL;
            obj->size --;
            return;
        }

        Node* pre = obj->next;
        int i;
        for (i = 1;i < index;i ++)
        {
            pre = pre->next;
        }
        Node* cur = pre->next;
        pre->next = cur->next;
        obj->size --;
        free(cur);
        cur = NULL;
        return;
    }

    void myLinkedListFree(MyLinkedList* obj) {
        int i;
        Node* cur;
        cur = obj->next;
        while (obj->size > 0)
        {
            cur = obj->next;
            obj->next = cur->next;
            free(cur);
            cur = NULL;
            obj->size --;
        }
        return;
    }

    /**
     * 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);
    */

为什么函数传入是传地址呢。 因为C语言函数传入参数是值传入。因为C语言函数传入参数是值传入。因为C语言函数传入参数是值传入。 传入变量的值,函数内只是得到了这个值,没法修改原来的内容。传入要修改的变量的地址,函数当作传进来一个指针,就可以修改指针所指地址上的值,实现修改传递到函数外。

一开始没用虚拟头节点,写的很难受,而且很多传入参数结点指针obj,没有传出参数,这就导致obj所指的物理空间的那个节点必须时刻是头节点,对于要在头部插入,可能就得插在第二个位置,然后和第一个互换val值,但这太麻烦了,修改为采用虚拟头节点方案: 虚拟头节点的val是size,next是实际头节点,可以采用分别定义的方法,但实际上节点和虚拟头节点是一样的结构。

原题目给的框架里面obj所指的物理位置不可改变,所以obj就适合作为虚拟头节点。

使用free(cur)后,为了防止cur变成野指针,要补充一句:cur = NULL

写完后有一处bug: 虽然是void不需要返回值,但也要养成写return;的习惯,有一个函数内我分类讨论了:

    void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
        if (obj->size <= index)
        {
            return;
        }

        if (index == 0)
        {
            Node* cur = obj->next;
            obj->next = cur->next;
            free(cur);
            cur = NULL;
            obj->size --;
            return;//-----------例如这里如果忘记return,会继续跑后面的,造成出错--------------
        }

        Node* pre = obj->next;
        int i;
        for (i = 1;i < index;i ++)
        {
            pre = pre->next;
        }
        Node* cur = pre->next;
        pre->next = cur->next;
        obj->size --;
        free(cur);
        cur = NULL;
    }