链式存储的基本实现

87 阅读4分钟

1、链式存储的基本实现

该部分主要通过leetcode编程练习整理,接下来结合代码注释来分别解释单链表和双链表(注意到编程网站中并没有按值查找。实际上也可以在leetcode结合复习即可)。可结合具体网址练习。网址如下:链表 - LeetBook

单链表

单链表的具体结构、概念和操作如下:

image-20220811175448488.png

1、构建单链表数据结构
class MyLinkedList {
    /*
     1、class默认属性为私有(struct里属性为公有)
     2、MyLinkedList里封装结点所具备的属性,并在私有属性上设置一个head结点和链表长度size。
    */
public:
    struct node {
        int val;
        node* next;
        //初始化一个节点,其数值为x,指针为空
        node(int x) : val(x), next(NULL) {}
    };
    //...
    //...具体方法省略...
    //...
private:
    int size;
    node* head;
};
2、具体方法
初始化
MyLinkedList() {
    head = new node(0);//初始化一个头结点,通过new将其放入
    size = 0;
}

在C++中,我们使用new关键字手动(由程序员手动)创建一个内存块在堆区(堆区的具体delete最终是由操作系统自动回收),设置的头结点没有size。

按位查找
int get(int index) {//index是根据计算机计数输入
        if (index<0 || index>size-1) {
            return -1;//获取失败
        }
        node* cur = (head->next);//设立一个辅助指针,指向最开始的head节点或者head->next都可
        for (int i = 0; i < index; i++) {
            //循环一次移动一次
            cur = cur->next;
        }
        return cur->val;
    }
首部添加节点
void addAtHead(int val) {
        //头结点只需要新建一个待插入结点即可
        node* temp = new node(val);
        temp->next = head->next;
        head->next = temp;
        size += 1;
    }

这部分很简单,并不需要辅助指针因为可以直接使用head

尾部添加节点
void addAtTail(int val) {
        //头结点采用头插法,所以与插入首部的区别在于先遍历到后面最后一个结点
        node* cur = head;
        /*
        *  2->5->7->(8),应该让cur指到8
        */
        while (cur->next) {
            cur = cur->next;
        }
        node* temp = new node(val);
        temp->next = cur->next;
        cur->next = temp;
        size += 1;
    }
插入节点
 void addAtIndex(int index, int val) {
        if (index == 0) addAtHead(val);
        else if (index == size) addAtTail(val);
        else if (index > 0 && index < size) {
            node* cur = head;
            for (int i = 0; i < index; i++) {
                //循环一次移动一次
                cur = cur->next;
            }
            node* temp = new node(val);
            //关键
            temp->next = cur->next;
            cur->next = temp;
            //关键
            size += 1;
        }
        else {
            return;
        }        
    }

在两个关键注释中就是插入节点的关键代码。其时间复杂度花费于查找上,所以插入时间复杂度为O(n)。其插入过程本身为O(1)

删除节点
void deleteAtIndex(int index) {
        if (index < 0 || index >= size) return;
        node* cur = head;
        for (int i = 0; i < index; i++) {
            //循环一次移动一次
            cur = cur->next;
        }
        cur->next = cur->next->next;
        size -= 1;
    }

双链表

双链表的基本结构、概念和操作如下:

image-20220811175556710.png

1、构建双链表数据结构
class MyLinkedList {
    //跟单链表一样,在里面设置结构体node。
    struct node {
        int val;
        node* next, * prior;
        node(int x) : val(x), next(NULL), prior(NULL) {}
    };
    //...
    //...
private:
    node* head;
    int size;
};
2、具体方法
初始化
public:
    MyLinkedList() {
        head = new node(0);
        size = 0;
    }

该部分跟单链表类似。

按位查找
    int get(int index) {
        if (index < 0 || index >= size) return -1;
        node* cur = head->next;
        while (index--) {
            cur = cur->next;
        }
        return cur->val;
    }

该部分跟单链表类似。

首部添加节点
    void addAtHead(int val) {
        //创建一个结点
        node* temp = new node(val);
        /*
            插入时应该是先处理后面再处理前面
        */
        if (head->next)
            //如果头结点后面有结点,需要将该结点的prior指针指向temp
            head->next->prior = temp;
        temp->next = head->next;
​
        temp->prior = head;
        head->next = temp;
        size++;
    }

在注释中提到,这里处理添加节点首先要判断head->next是否有节点。

尾部添加节点
    void addAtTail(int val) {
        node* cur = head;
        while (cur->next) {
            cur = cur->next;
        }
        //此处代码跟addAtHead一致
        node* temp = new node(val);
        cur->next = temp;
        temp->prior = cur;
        temp->next = NULL;
        size++;
​
    }

同样的,其cur指向最后一个元素,在最后面添加元素时注意temp的prior和next指向赋值。

插入节点
    void addAtIndex(int index, int val) {
        if (index == 0) addAtHead(val);
        else if (index == size) addAtTail(val);
        else if (index > 0 && index < size) {
            node* cur = head->next;
            while (index--) {
                cur = cur->next;
            }
            //指针指到temp所插入的后一个节点
            node* temp = new node(val);
            
            temp->next = cur;
            temp->prior = cur->prior;
            cur->prior->next = temp;
            cur->prior = temp;
            size++;
        }
        else {
            return;
        }
    }
删除节点
    void deleteAtIndex(int index) {
        if (index < 0 || index >= size) return;
        node* cur = head->next;
        while (index--) {
            cur = cur->next;
        }
        if (cur->next) {
            //删除节点不是最后一个元素
            cur->next->prior = cur->prior;
            cur->prior->next = cur->next;
        }
        else {
            cur->prior->next = NULL;
        }
        size--;
    }

这里处理时跟前面的首部添加节点类似,都需要判断该index所处的节点是否为最后一个节点。