Leetcode---单链表

单链表

本博客的题均为leetcode上的摘录,都是我的刷题答案,仅做参考,若有错误,欢迎指正

前面提到的数组和字符串跟我们这里要说的链表都属于数据结构里面的线性表;

所谓线性表,就是在内存中存储的数据元素都是连续的,一一对应的,也可以理解成是一串一串的。计算机通常将线性表的数据按照前后顺序将他们连接在一起,方便程序遍历和操作。

线性表又分为顺序存储结构和链式存储结构:

        顺序存储结构顾名思义,就是该线性表的所有元素都是按照内存地址顺序连续存储的,通常是在内存地址里面找到一个起始地址,然后根据每个元素占用的地址位置大小,以占位的形式占用连续的内存地址空间,然后按照顺序将数据元素逐个存储到内存地址当中;这样的存储结构所需的内存空间是固定的,一般在定义或者初始化的时候会确定其大小,然后在内存地址当作占位所需的空间;由于他们的内存地址都是连续的,所以只需要知道起始地址,就能根据索引直接访问该数据结构里面的所有数据元素;顺序存储结构主要的体现形式是数组和字符串,他们都是按照内存地址顺序在一块内存中连续存储多个数据元素的结构。

        链式存储结构相当于顺序存储结构来说,他的所有数据元素的存储地址并不是连续的,而是杂乱无章的,而且地址在哪是程序员无法确定的,都是由计算机来自由分配,程序员只是申请指定大小的内存地址空间而已。链式存储结构最主要的体现就是链表,他的每一个数据元素的存储空间并不只是该数据元素的类型大小所占的内存空间,他是数据元素的值和指向下一个数据元素的指针的集合,就是带有数据元素数值和指向下一个数据元素的指针构成的结构体来实现,通常这样的一个结构体被称为一个数据元素节点。一个链表是由若干个节点连接在一起的,每个节点存储一个数据元素和节点结构体指针。每个链表还会有一个头指针,该指针指向链表的第一个节点(也可以是头结点,头结点里面包含一个头指针指向链表第一个节点的位置,而且头结点的数据元素还有包含链表的长度值)。

上图所示,头指针指向第一个节点a1,和a1在同一个节点结构体里面的还有一个节点结构体指针,它指向下一个节点,这样以此类推就能将一个线性表的所有元素按顺序连接起来,而不需要连续的一块内存地址。

以下文本均从leetcode的题目中摘录,以示说明。

  1、添加操作 - 单链表

如果我们想在给定的结点 prev 之后添加新值,我们应该:

  1. 使用给定值初始化新结点 cur;
  2. 将 cur 的“next”字段链接到 prev 的下一个结点 next
  3. 将 prev 中的“next”字段链接到 cur 。

与数组不同,我们不需要将所有元素移动到插入元素之后。因此,您可以在 O(1) 时间复杂度中将新结点插入到链表中,这非常高效。

示例


让我们在第二个结点 6 之后插入一个新的值 9。

我们将首先初始化一个值为 9 的新结点。然后将结点 9 链接到结点 15。最后,将结点 6 链接到结点 9。

插入之后,我们的链表将如下所示:

在开头添加结点

众所周知,我们使用头结点来代表整个列表。

因此,在列表开头添加新节点时更新头结点 head 至关重要。

  1. 初始化一个新结点 cur;
  2. 将新结点链接到我们的原始头结点 head
  3. 将 cur 指定为 head

例如,让我们在列表的开头添加一个新结点 9。

  1. 我们初始化一个新结点 9 并将其链接到当前头结点 23。
  2. 指定结点 9 为新的头结点。 

 

删除操作 - 单链表


如果我们想从单链表中删除现有结点 cur,可以分两步完成:

  1. 找到 cur 的上一个结点 prev 及其下一个结点 next;
  2. 接下来链接 prev 到 cur 的下一个节点 next。

在我们的第一步中,我们需要找出 prev 和 next。使用 cur 的参考字段很容易找出 next,但是,我们必须从头结点遍历链表,以找出 prev,它的平均时间是 O(N),其中 N 是链表的长度。因此,删除结点的时间复杂度将是 O(N)

空间复杂度为 O(1),因为我们只需要常量空间来存储指针。

示例


让我们尝试把结点 6从上面的单链表中删除。

1. 从头遍历链表,直到我们找到前一个结点 prev,即结点 23

2. 将 prev(结点 23)与 next(结点 15)链接

结点 6 现在不在我们的单链表中。

删除第一个结点


如果我们想删除第一个结点,策略会有所不同。

正如之前所提到的,我们使用头结点 head 来表示链表。我们的头是下面示例中的黑色结点 23。

如果想要删除第一个结点,我们可以简单地将下一个结点分配给 head。也就是说,删除之后我们的头将会是结点 6。

链表从头结点开始,因此结点 23 不再在我们的链表中。

下面为设计链表的题目,代码由本人独自完成,备有注释,仅做参考

设计链表

设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 nextval 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。

在链表类中实现这些功能:

  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1
  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
//C++实现
class MyLinkedList {
    
    //定义一个链表节点的结构,包含数据变量和指针变量
    typedef struct SinglyListNode{
        int val;
        SinglyListNode *next;
        SinglyListNode(int x) : val(x), next(NULL) {}
    }node;
    node *head;     //定义头指针,它指向链表的第一个元素
    
public:
    /** Initialize your data structure here. */
    MyLinkedList() {
        head=NULL;      //初始化时,头指针指向NULL,即链表为空
    }
    
    /** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
    int get(int index) {
        node *p;        
        p=head;     //定义一个节点指针p,让他指向head指向的地址,即将head存储的地址值赋给p,[p指向第一个节点]
        int i=0;    
        while(p && i<index)     //index应该是从1开始计数,i技术到index,此时p就指向了index
        {
            p=p->next;
            i++;
        }
        if(p && i==index)       //if-else语句时,尽量将满足条件的放在if,其他的条件放在else里面
            return p->val;
        else
            return -1;
    }
    
    /** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
    void addAtHead(int val) {
        
        node *inode = new node(val);  //定义节点指针,并申请内存,inode即为指向该内存的指针,inode为新节点地址值
        inode->next=head;             //inode->next等效于(*inode).next
        head=inode;                   //head指向inode,inode作为第一个节点
    }
    
    /** Append a node of value val to the last element of the linked list. */
    void addAtTail(int val) {
        if(head==NULL)      //链表为空,直接添加,用head指向1inode即可
        {
            node *inode = new node(val);
            head=inode;
        }
        else
        {
            node *p;        //定义临时的节点指针来遍历链表
            p=head;         //p先指向第一个节点
            while(p->next)  //判断p->next是否为NULL,如果p->next,表示到链表的末节点了
            {
                p=p->next;
            }
            node *inode = new node(val);
            p->next=inode;  //此时p指向链表末节点,只需将p->next指向新建的节点inode即可
        }
    }
    
    /** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
    //在第index-1和第index个节点之间插入新节点
    void addAtIndex(int index, int val) {
        if(index<=0)            //index<=0,直接当做头结点添加,调用addAtHead()函数
            addAtHead(val);
        else
        {
            node *p;
            p=head;
            int i=1;
            while(p && i<index)     //遍历链表,知道P指向index的节点
            {
                p=p->next;
                i++;
            }
            if(i==index && p)       //确保遍历到第index个节点,并且p指向的节点不为NULL
            {
                node *inode = new node(val);
                inode->next=p->next;
                p->next=inode;
            }
        }
    }
    
    /** Delete the index-th node in the linked list, if the index is valid. */
    void deleteAtIndex(int index) {
        node *p;
        node *q;
        p=head;
        int i=1;
        if(head==NULL)      //链表为空,不必进行删除操作
            return;
        else if(index==0)   //index为0,删除第一节点
        {
            q=p;
            head=q->next;
            delete q;
        }
        else                //遍历到index个节点
        {
            while(p->next && i<index)   
            {
                p=p->next;
                i++;
            }
            if(p->next && i==index) //保证第index个节点不为NULL,
            {
                q=p->next;          //删除p->next
                p->next=q->next;
                delete q;
            }
        }
    }
    
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值