zoukankan      html  css  js  c++  java
  • 数组实现单链表

    单链表常见的实现方法有两种,一种方式是定义一个结构体表示链表节点。比如:

    struct node{
          int val;
          node* next;
    };
    

    然后就是通过next指针将链表的所有节点连接起来。如果涉及到链表节点的插入和删除操作,则只需要修改链表节点的指针即可。

    这种方式有个明显的缺点,就是不能随机存取。如果要在某个节点之后插入或者删除节点,复杂度是O(n),因为要从头开始逐个遍历到需要插入或者删除的节点(通过next指针找)。所以用结构体实现的单链表缺点就是太慢了。当然还有一个原因就是C++里要new一个node比较慢。总之就是慢~

    因此算法题中使用单链表往往是通过数组实现,数组实现的单链表可以通过下标来索引节点,可以直接通过下标找到某个节点的值和下一个节点的,因此数组实现的单链表的最大优点就是快(插入和删除操作都是O(1)的时间复杂度),毕竟数组的特点就是随机存取嘛。

    这里结合一道题目来看数组实现单链表。
    原题链接

    这道题题意非常直白。就是需要实现一个单链表,这个链表支持三个操作:(1)在链表的表头插入一个节点;(2)删除某个数后面的数;(3)在某个数后面加入一个数(即插入节点)。
    输入有若干行,每行都有可能是三个操作的其中之一,最后的输出就是经过所有操作之后从头到尾将链表输出。

    来模拟一下样例吧。

    来看一下实现。
    要实现三个操作,我们可以额外定义三个函数分别表示上面三个操作。
    这里分别命名为insertBeforeHead(int x)Insert(int k, int x)Delete(int k)。这里的x就是要插入的值,k就是要插入/删除节点的前一个位置(也就是在第k个节点之后进行插入/删除)。

    这里要注意,第k个节点并不是当前链表从前往后数,而是从最开始计算,插入的第k个节点(也就是说前面插入的节点被删除了,并不会重新计算k)。

    要用数组表示单链表,和结构体实现链表一样,每个节点都要有值和记录下一个节点的“指针”,因此我们可以开两个数组val和nex分别表示节点的数值和下一个节点的位置。
    由于需要对链表头做操作(在链表头插入节点)和记录已经插入的节点的个数(因为要在第k个节点之后进行插入/删除操作),因此我们需要用两个“指针”记录头节点和当前操作的节点是第几个节点。
    简单的说,head指向的就是链表头节点(第一个节点,不是附加头节点)的下标,idx表示当前用到的是第几个节点。

    const int N = 1e5 + 5;
    int val[N], nex[N], head, idx;    //head表示头节点的下标,idx表示已经插入的节点的个数
    

    链表需要有初始化操作,不然headidx的值可能是不确定的。

    void init() {
          head = -1;      //注意:这里的head并不是虚拟头节点,指向的就是链表的第一个节点,这样做主要是为了方便在头节点插入数据,最开始没有插入节点,head指向-1
          idx = 0;        //还没插入节点,idx为0
    }
    

    然后看一下在链表头节点(之前)插入节点。
    由于head记录了原来的头节点,我们希望新插入一个节点,这个节点的值为x,且这个节点的**next指针**指向原来的head
    看一下代码:

    void insertBeforeHead(int x) {
          val[idx] = x;      //新插入一个值为x的节点
          nex[idx] = head;   //x的下一个节点是原来的头节点,表示在头节点之前插入x
          head = idx;        //head指针指向新节点
          ++idx;             //更新插入节点的个数
    }
    

    看一下如何在第k个节点之后插入一个节点。
    首先肯定要创建一个值为x的节点:val[idx] = x;
    且这个新节点的next指针指向第k+1个节点:nex[idx] = nex[k]. (nex[k]是原来的第k+1个节点,让新插入的节点指向这个节点)。
    然后k的next指针也要更新,指向x: nex[k] = idx;
    再更新一下已经插入的节点的个数:++idx;
    这样就得到在第k个节点之后插入节点x的代码:

    void Insert(int k, int x) {
          val[idx] = x;
          nex[idx] = nex[k];
          nex[k] = idx;
          ++idx;
    }
    

    然后就剩下在第k个节点之后删除节点的函数啦:
    这个很简单,要让第k个节点之后的值不存在,就直接让第k个节点的next指针指向第k+2个节点,也就是跳过了第k+1个节点,这样第k+1个节点就社会性死亡了。

    void Delete(int k) {
          nex[k] = nex[nex[k]];
    }
    

    这道题的完整代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N = 1e5 + 5;
    int val[N], nex[N], head, idx;
    
    void init() {
        head = -1;
        idx = 0;
    }
    
    void insertBeforeHead(int x) {
        val[idx] = x;
        nex[idx] = head;
        head = idx;
        ++idx;
    }
    
    //在下标为k的节点后插入x
    void Insert(int k, int x) {
        val[idx] = x;
        nex[idx] = nex[k];
        nex[k] = idx;
        ++idx;
    }
    
    //删除下标为k的节点的下一个节点
    void Delete(int k) {
        nex[k] = nex[nex[k]];
    }
    
    int main() {
        int M;
        cin >> M;            //操作的个数
        init();              //操作之前需要对head和idx指针进行初始化
        while(M--) {
            char op;         //当前进行的操作
            int k, x;
            cin >> op;
            if(op == 'H') {     //在头节点之前插入一个节点
                cin >> x;
                insertBeforeHead(x);
            } else if(op == 'I') {    //在第k个节点之后插入一个节点
                cin >> k >> x;
                Insert(k - 1, x);            //由于idx初始为0,所以第k个节点的下标为k - 1
            } else if(op == 'D') {
                cin >> k;
                if(k == 0) {                  
                    head = nex[head];
                } else {
                    Delete(k - 1);            //由于idx初始为0,所以第k个节点的下标为k - 1
                }
            }
        }
        for(int i = head; i != -1; i = nex[i]) {
            cout << val[i] << ' ';
        }
        cout << endl;
        return 0;
    }
    
  • 相关阅读:
    SQL性能优化(Oracle)
    反射基础详解
    线程同步
    死锁产生的原因和解锁的方法
    java中的sleep()和wait()的区别
    线程Thread的基础知识学习
    cookies和session的优缺点
    js中的clientWidth offsetWidth scrollWidth等的含义
    手机端的META你知道多少?
    鼠标点击input框后里面的内容就消失
  • 原文地址:https://www.cnblogs.com/linrj/p/13307798.html
Copyright © 2011-2022 走看看