数据结构篇——链表

数据结构篇——链表

本次我们介绍基础算法中的区间合并,我们会从下面几个角度来介绍:

  • 单链表
  • 双链表

单链表

我们会在这里介绍单链表

单链表简介

我们首先来简单介绍一下单链表:

  • 单链表就是一条长链,我们会延一个固定的顺序来获得或增添值
  • 我们在算法计算中,通常会采用数组来模拟单链表来完成一些操作

单链表的作用:

  • 单链表的作用其实是用来设计邻接表,由n个单链表来组成邻接表
  • 而邻接表的作用是用来存储后续我们所学习的图和数

单链表基本组成

我们这里的单链表由以下几部分组成:

  • head:头节点,用于存储下一个节点的位置(-1表示空)

  • e[n]: 数据数组,用来存储下标为n的数的值

  • ne[n]:next数组,用来存储第n个数的下一个数的节点坐标

  • idx:当前数的下标,我们通常在使用过一个下标后对idx++来获得一个新的下标

我们给出一张单链表的基本图:

单链表各类操作

首先我们要对单链表进行初始化操作:

public void init(){
    head = -1;
    idx = 0;
}

其次我们需要对单链表进行添加删除操作:

// 头插法(在第一位增加一个数x)
public void insertHead(int x){
	// 我们首先赋值
    e[idx] = x;
    // 我们将head的值赋给idx的下一位(因为head是第一位的存储点位,我们把idx变为第一位,后续就要存储第二位的存储点位)
    ne[idx] = head;
    // 我们将head的值变为idx(因为idx变为了第一位,head要指向第一位,所以需要指向idx)
    head = idx;
    // 记得idx++,使其变为一个新的下标节点
    idx++;
}

// 增加操作(在第k位后面增加一个数x)
public void add(int k,int x){
    // 我们首先赋值
    e[idx] = x;
    // 我们让x的下一位变为k的下一位
    ne[idx] = ne[k];
    // 我们让k的下一位变为idx
    ne[k] = idx;
    // 记得idx++,使其变为一个新的下标节点
    idx++;
}

// 删除操作(删除第k位后面的那个数)
public void delete(int k){
    // 我们直接控制k的下一位跳过k的下一位即可(这里会造成内存泄漏,但是由于是算法,我们就不清楚空余出来的idx了)
    ne[k] = ne[ne[k]];
}

我们给出相关操作的展示图:

单链表题型展示

我们给出一道简单的单链表题型:

  • 实现一个单链表,链表初始为空,支持三种操作:
  • 向链表头插入一个数
  • 删除第k个插入的数后面的数
  • 在第k个插入的数后插入一个数
  • 现在要对该链表进行M次操作,进行完所有操作后,从头到尾输出整个链表

我们给出实际代码:

import java.util.Scanner;

public class TableSingle {

    // 设置N,Head,e[],ne[],idx等
    public final static int N = 100010;

    public static int head;
    public static int idx;
    public static int[] e = new int[N];
    public static int[] ne = new int[N];

    public static void main(String[] args) {
        // 首先获得执行次数m
        Scanner scanner = new Scanner(System.in);

        int m = scanner.nextInt();
        
        init();

        while (m-- > 0) {
            int k,x;

            // 执行操作类型
            String op = scanner.next();

            // 执行操作
            if (op.equals("H")){
                x = scanner.nextInt();
                insertInHead(x);
            }else if (op.equals("I")){
                k = scanner.nextInt();
                x = scanner.nextInt();
                add(k-1,x);
            }else if (op.equals("D")){
                k = scanner.nextInt();
                // 注意:这里如果k==0,需要删除第一个点
                if (k == 0)
                    head = ne[head];
                else
                    delete(k - 1);
            }
        }

        // 输出一下
        int i = head;
        while (i != -1) {
            System.out.print(e[i] + " ");
            i = ne[i];
        }

    }

    /**
     * 初始化
     */
    public static void init(){
        head = -1;
        idx = 0;
    }

    /**
     * 头插法
     * @param x
     */
    public static void insertInHead(int x){
        // 我们首先赋值
        e[idx] = x;
        // 我们将head的值赋给idx的下一位(因为head是第一位的存储点位,我们把idx变为第一位,后续就要存储第二位的存储点位)
        ne[idx] = head;
        // 我们将head的值变为idx(因为idx变为了第一位,head要指向第一位,所以需要指向idx)
        head = idx;
        // 记得idx++,使其变为一个新的下标节点
        idx++;
    }

    /**
     * 添加操作
     * @param k
     * @param x
     */
    public static void add(int k,int x){
        // 我们首先赋值
        e[idx] = x;
        // 我们让x的下一位变为k的下一位
        ne[idx] = ne[k];
        // 我们让k的下一位变为idx
        ne[k] = idx;
        // 记得idx++,使其变为一个新的下标节点
        idx++;
    }

    /**
     * 删除操作
     * @param k
     */
    public static void delete(int k){
        // 我们直接控制k的下一位跳过k的下一位即可(这里会造成内存泄漏,但是由于是算法,我们就不清楚空余出来的idx了)
        ne[k] = ne[ne[k]];
    }
}

双链表

我们会在这里介绍双链表

双链表简介

我们首先来简单介绍一下双链表:

  • 单链表就是一条长链,我们在表中的值的两侧都具有一个可以传到下一个点的链条
  • 我们在算法计算中,通常会采用数组来模拟单链表来完成一些操作

双链表的作用:

  • 通常是用来优化某些问题

双链表基本组成

我们这里的双链表由以下几部分组成:

  • head:头节点,下标为0

  • tail:尾节点,下标为1

  • e[n]: 数据数组,用来存储下标为n的数的值

  • le[n]:存储该点左侧的下标

  • re[n]:存储该点右侧的下标

  • idx:当前数的下标,我们通常在使用过一个下标后对idx++来获得一个新的下标

我们给出一张双链表的基本图:

双链表各类操作

首先我们要对双链表进行初始化操作:

public static void init(){
	head = 0;
    tail = 1;
    r[head] = 1;
    l[tail] = 0;
}

我们这里给出双链表的一种添加和删除操作:

// 在k的右侧添加x
public static void add(int k,int x){
    
    // 赋值
    e[idx] = x;
    
    // 我们依次修改三个点之间的四条链的指向方向即可(可以画图~)
    r[idx] = r[k];
    l[idx] = k;
    l[r[idx]] = idx;
    r[k] = idx;
    
    // 记得idx++
    idx++;
}

// 删除第k个点
public static void delete(int k){
    // 我们只需要修改k左右两侧的点不指向k即可
    l[r[k]] = l[k];
    r[l[k]] = r[k];
}

双链表题型展示

我们给出一道简单的双链表题型:

  • 实现一个双链表,双链表初始为空,支持5种操作:

  • 在最左侧插入一个数;

  • 在最右侧插入一个数;

  • 将第k个插入的数删除;

  • 在第k个插入的数左侧插入一个数;

  • 在第k个插入的数右侧插入一个数

  • 现在要对该链表进行m次操作,进行完所有操作后,从左到右输出整个链表。

我们给出实际代码:

import java.util.Scanner;

public class TableDouble {

    // 创建初始值
    public static final int N = 100010;

    public static int idx;
    public static int head;
    public static int tail;
    public static int[] e = new int[N];
    public static int[] l = new int[N];
    public static int[] r = new int[N];

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int m = scan.nextInt();

        init();

        while(m -- > 0){
            String s = scan.next();
            char op = s.charAt(0);
            if(op == 'L'){
                int x = scan.nextInt();
                addInRight(head,x);
            }else if(op == 'R'){
                int x = scan.nextInt();
                addInRight(l[tail],x);
            }else if(op == 'D'){
                int k = scan.nextInt();
                delete(k + 1);
            }else if(s.equals("IR")){
                int k  = scan.nextInt();
                int x = scan.nextInt();
                addInRight(k + 1,x);
            }else {
                int k = scan.nextInt();
                int x = scan.nextInt();
                addInRight(l[k+1],x);
            }
        }
        for(int i = r[0];i != 1; i= r[i]){
            System.out.print(e[i]  + " ");
        }
    }

    /**
     * 初始化
     */
    public static void init(){
        head = 0;
        tail = 1;
        idx = 2;
        r[head] = tail;
        l[tail] = head;
    }

    /**
     * 在k的右侧添加x
     * @param k
     * @param x
     */
    public static void addInRight(int k,int x){
        e[idx] = x;
        r[idx] = r[k];
        l[idx] = k;
        l[r[idx]] = idx;
        r[k] = idx;
        idx++;
    }

    /**
     * 删除k
     * @param k
     */
    public static void delete(int k){
        r[l[k]] = r[k];
        l[r[k]] = l[k];
    }

}

结束语

好的,关于数据结构篇的链表就介绍到这里,希望能为你带来帮助~