Java实现顺序表和链表结构

目录
  • 前言:
  • 顺序表
    • 定义:
    • 实现方法:
    • 代码实现:
  • 链表
    • 定义:
    • 分类:
    • 实现方法:
    • 代码实现:
  • 顺序表 & 链表
  • 总结

前言:

线性表(linear list)是n个具有相同特性的数据元素的有限序列。

线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串。

顺序表

定义:

用一段物理地址连续的存储单元依次存储数据元素的线性结构(逻辑上连续,物理上也连续)

(1)静态顺序表:使用定长数组存储。

(2)动态顺序表:使用动态开辟的数组存储

【注意】静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用,所以相比之下,动态数组更为灵活,可根据需要动态分配空间大小

实现方法:

增、删、改、查

增加操作:从头部插入、从尾部插入、在任意索引位置处插入

删除操作:根据索引删除元素、根据元素值删除第一个出现的该元素、根据元素值删除所有该值元素

查找操作:根据元素值查找是否存在某元素、根据索引下标返回该处元素值、根据元素值返回索引下标

修改操作:根据索引下标修改该处元素

代码实现:

public class MyArray {
    private int[]data;
    private int size;
//    无参构造
    public MyArray(){
        this.data=new int[5];
    }
//    有参构造
    public MyArray(int n){
        this.data=new int[n];
    }
//    grow方法用于扩充内存
    private void grow() {
        int[] newdata= Arrays.copyOf(data,size*2);
        this.data=newdata;
    }
//    toString方法输出顺序表内容
    public String toString(){
        String str="[";
        for (int i = 0; i <size ; i++) {
            str+=data[i];
            if(i!=size-1){
            str+=",";
            }
        }
        str+="]";
        return str;
    }
//    尾插法:
    public void addLast(int value){
        if(size== data.length){
            grow();
        }
        data[size]=value;
        size++;
    }
//    头插法:
    public void addFirst(int value){
        if(size==data.length){
            grow();
        }
        for (int i = size-1; i >=0; i--) {
            data[i+1]=data[i];
        }
        data[0]=value;
        size++;
    }
//    中间任意位置插入:
    public void addIndex(int index,int value){
        if(size==data.length)
            grow();
        if(index<0||index>size){
            System.err.println("插入位置不合理!");
            return;
        }
        else {
            for (int i = size - 1; i >= index; i--) {
                data[i + 1] = data[i];
            }
            data[index] = value;
            size++;
        }
    }
//    查看当前数组中是否存在该元素
    public boolean contains(int value){
        for (int i = 0; i < size; i++) {
            if(data[i]==value)
                return true;
        }
        return false;
    }
//    查找当前数组元素对应的下标
    public int getIndex(int value){
        for (int i = 0; i < size; i++) {
            if(data[i]==value){
                return i;
            }
        }
        return -1;

    }
//    查找数组下标为index的元素
    public int getValue(int index) {
        if (index < 0 || index > size - 1) {
            System.out.println("输入下标不合理");
            return -1;
        }

        return data[index];
    }
//    根据索引删除元素,注意将最后一个元素置为0
    public void removeIndex(int index){
        if(index<0||index>=size){
            System.err.println("输入不合法!");
        }
        for (int i = index; i <size-1; i++) {
            data[i]=data[i+1];
        }
        size--;
        data[size]=0;
    }
//    删除第一个元素值为value的元素
    public void removeValueOnce(int value){
        int a=getIndex(value);
      removeIndex(a);

        }
//        删除所有元素值为value的元素
    public void removeValueAll(int value){
        for (int i = 0; i < size; i++) {
           while(i!=size||data[i]==value)
               removeIndex(i);

        }

    }
//    根据索引修改元素
    public void recompose(int index,int newValue){
        if(index<0||index>=size){
            System.err.println("输入不合法!");
        }

        data[index]=newValue;
    }
    }

链表

定义:

逻辑上连续,物理上非连续存储。

链表由一个个节点组成,每个节点存储该节点处的元素值val 以及下一个节点的地址next,由地址next实现逻辑上连续

分类:

分类方式:

(1)单链表、双链表

(2)带虚拟头节点、不带虚拟头节点

(3)循环链表、非循环链表

按不同分类方式组合有8种:

非循环四种:

(1)不带虚拟头节点的单向链表(非循环)

(2)带虚拟头节点的单向链表(非循环)

(3)不带虚拟头节点的双向链表(非循环)

(4)带虚拟头节点的双向链表(非循环)

循环四种:

(5)不带虚拟头节点的单向链表(循环)

(6)带虚拟头节点的单向链表(循环)

(7)不带虚拟头节点的双向链表(循环)

(8)带虚拟头节点的双向链表(循环)

其中:

(1)不带虚拟头节点的单向链表(非循环):结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。这种结构在笔试面试中出现很多

(7)不带虚拟头节点的双向链表(循环):在Java的集合框架库中LinkedList底层实现就是无头双向循环链表

实现方法:

增、删、查、改     和顺序表实现方法基本一样;

唯一注意:带虚拟头节点与不带虚拟头节点相比,带虚拟头节点避免了考虑头节点为空的特殊情况

代码实现:

(1)不带虚拟头节点的单链表:

class Node {
//    val表示存储的数值,next表示下一个节点的地址
    int val;
    Node next;

    //    构造方法
    public Node(int val) {
        this.val = val;
    }
}

public class SingleList {
    //    size表示节点个数
//    head表示头节点
    private int size;
    private Node head;

    //定义toString方法以输出链表内容
    public String toString() {
        String str = "";
        Node node = head;
        while (node != null) {
            str += node.val;
            str += "->";
            node = node.next;
        }
        str += null;
        return str;
    }

    //将判断输入的索引是否合法抽象为方法islegal
    public boolean islegal(int index) {
        if (index < 0 || index > size) {
            return false;
        } else {
            return true;
        }
    }

    //    头插法
    public void addHead(int value) {
        Node node = new Node(value);
        if (head == null) {
            head = node;

        } else {
            node.next = head;
            head = node;
        }
        size++;
    }

    //    中间任意位置插入
    public void addIndex(int index, int val) {
        if (!islegal(index)) {
            System.out.println("输入数据不合法!");
            return;
        }
        if (index == 0) {
            addHead(val);
            return;
        }
        Node node = new Node(val);
        Node pre = head;

        for (int i = 0; i < index - 1; i++) {
            pre = pre.next;
        }
        node.next = pre.next;
        pre.next = node;
        size++;
        return;
    }

    //    尾插法
    public void addLast(int val) {
        if (head == null) {
            addHead(val);
        } else {
            addIndex(size, val);
        }
    }

    //    删除指定索引位置的元素
    public void removeIndex(int index) {

        if (islegal(index)) {
            if (index == 0) {
                Node temp = head;
                head = head.next;
                temp.next = null;
                size--;
                return;
            } else {

                Node pre = head;
                for (int i = 0; i < index - 1; i++) {
                    pre = pre.next;
                }
                Node cur = pre.next;
                pre.next = cur.next;
                cur.next = null;

                size--;
            }

        } else {
            System.out.println("输入数据不合法!");
        }
    }

    //    根据元素值删除元素,且只删除第一次出现的该元素
    public void removeValueOnce(int val) {
        if (head.next != null && head.val == val) {
            removeIndex(0);
        } else {
            Node pre = head;
            while (pre.next != null) {
                if (pre.next.val == val) {
                    pre.next = pre.next.next;
                    pre.next.next = null;
                    size--;
                    return;

                }
                pre = pre.next;
            }
        }
    }

    //    根据元素值删除元素,且删除所有该元素
    public void removeValueAll(int val) {
        while (head.next != null && head.val == val) {
            Node temp = head;
            head = head.next;
            temp = null;
            size--;
        }
        if (head == null) {
            return;
        } else {
            Node pre = head;
            while (pre.next != null) {
                if (pre.next.val == val) {
                    pre.next = pre.next.next;
                    size--;
                    return;

                } else {
                    pre = pre.next;
                }
            }
        }
    }

    //       根据元素值删除元素,且删除所有该元素并返回头节点(带虚假节点)
    public Node removeValueAllWithDummy(int val) {
        Node dummyHead = new Node(-1);
        dummyHead.next = head;
        Node pre = dummyHead;
        while (pre.next != null) {
            if (pre.next.val == val) {
                Node cur = pre.next;
                pre.next = cur.next;
                cur.next = null;
                size--;
            } else {
                pre = pre.next;
            }
        }
        return dummyHead.next;

    }

    //    根据索引查元素值
    public int get(int index) {
        if (islegal(index)) {
            Node cur = head;
            for (int i = 0; i < index; i++) {
                cur = cur.next;
            }
            return cur.val;
        } else {
            System.out.println("输入数据不合法!");
            return -1;
        }
    }

    //    能否查到给定的某元素值(自己写的,好像很复杂)
    public boolean contains(int val) {
        boolean a = false;
        if (head == null) {
            System.out.println("该链表为空!");
            return false;
        } else {
            Node node = head;

            for (int i = 0; i < size; i++) {
                if (node.val == val) {
                    a = true;
                }
                node = node.next;
            }
        }
        return a;
    }

    //    能否查到给定的某元素值,老师写的方法
    public boolean contains2(int val) {
        for (Node temp = head; temp != null; temp = temp.next) {
            if (temp.val == val) {
                return true;
            }
        }
        return false;
    }

    //    根据索引修改元素值
    public void set(int index, int newValue) {
        if (islegal(index)) {
            Node node = head;
            for (int i = 0; i < index; i++) {
                node = node.next;
            }
            node.val = newValue;
            return;
        }
        System.out.println("输入数据不合法!");
    }
}

(2)带虚拟头节点的单链表

以在指定索引位置插入元素为例,理解虚拟头节点的作用即可

public class SingleListWithDummyHead {
    Node dummyNode=new Node(-1);
    int size;
//    在指定位置插入元素,因为有虚拟头节点故不用考虑index=0的情况,全部为在中间位置插入的情况
    public void addIndex(int index,int value){
//        先判断index是否合法
        if(index<0||index>size){
            System.out.println("illegal");
            return;
        }
        Node a=new Node(value);
        Node pre=dummyNode;
        for(int i=0;i<index;i++){
            pre=pre.next;
        }
        a.next=pre.next;
        pre.next=a;
        size++;
    }
}

(3)带虚拟头节点的双链表

public class DoubleLinkedList {
    private int size;
    private Node dummyHead = new Node(-1);
    private Node tail;

    //    定义toString方法用以方便输出
    public String toString() {
        String s = "";
        Node node = dummyHead.next;
        while (node != null) {
            s = s + node.val;
            s = s + "->";
            node = node.next;
        }
        s += "null";
        return s;
    }

    //    判断输入的索引是否合法
    private boolean isRange(int index) {
        if (index < 0 || index >= size) {
            System.out.println("输入不合法!");
            return false;
        } else
            return true;
    }

    //    头插法
    public void addHead(int val) {
        Node a = new Node(val, dummyHead, dummyHead.next);
//链表为空
        if (dummyHead.next == null) {
            tail = a;
            dummyHead.next = a;
        }
//        否则链表不为空
        else {
            dummyHead.next.prev = a;
            dummyHead.next = a;
        }
        size++;
    }

    //    尾插法
    public void addTail(int val) {
        Node a = new Node(val, tail, null);
//        链表为空
        if (dummyHead.next == null) {
            dummyHead.next = a;
        }
//        链表不为空
        else {
            tail.next = a;
        }
        tail = a;
        size++;
    }

    //    中间位置插入
    public void addMiddle(int index, int val) {
//      判断插入位置合理否
        if (index < 0 || index > size) {
            System.out.println("输入不合法!");
        }
//        头部插入
        else if (index == 0) {
            addHead(val);
        }
//        尾部插入
        else if (index == size) {
            addTail(val);
        }
//        中间任意位置插入
        else {
//先找到要插入位置的前一个元素,可另一个方法找该元素
            Node a = new Node(val, find(index), find(index).next);
            find(index).next.prev = a;
            find(index).next = a;
            size++;
        }
    }

    //    这里find的方法是找到index位置的前一个节点元素
    public Node find(int index) {
        Node f = dummyHead;
        for (int i = 0; i < index; i++) {
            f = f.next;
        }
        return f;

    }

    //    根据索引删除指定位置的元素
    public void removeIndex(int index) {
        if (index < 0 || index >= size) {
            System.out.println("输入不合法!");
            return;
        } else {
            find(index).next.next.prev = find(index);
            find(index).next = find(index).next.next;
            size--;
        }
    }

    //    删除指定节点
    private void deleteNode(Node node) {
//        分治思想,先处理node与左边节点,再处理node与右边节点
        Node pre = node.prev;
        Node next = node.next;
//        处理左边,因为有虚拟头节点,故不用另讨论为头节点的情况
        pre.next = next;
        node.prev = null;
//       处理右边
        if (next == null) {
            tail = pre;
        } else {
            next.prev = pre;
            node.next = null;
        }
        size--;

    }

    //    删除指定元素值(只删除第一次出现的)
    public void removeValueOnce(int val) {
        for (Node a = dummyHead.next; a != null; a = a.next) {
            if (a.val == val) {
                deleteNode(a);
                return;
            }
        }
        System.out.println("链表中无该元素故无法删除");

    }

    public void removeValueAll(int val) {
        for (Node a = dummyHead.next; a != null; ) {
            if (a.val == val) {
                Node b = a.next;
                deleteNode(a);
                a = b;
            } else a = a.next;
        }
    }

    //    根据索引查找元素值
    public int get(int index) {
        if (isRange(index)) {
            return find(index).next.val;
        } else {
            return -1;
        }
    }

    //    查找是否存在某元素
    public boolean contains(int val) {
        Node a = dummyHead;
        while (a.next != null) {
            if (a.next.val == val) {
                return true;
            }
            a = a.next;
        }
        return false;
    }

    //    修改,将指定位置元素修改为另一值
    public void set(int index, int newValue) {
        if (isRange(index)) {
            find(index).next.val = newValue;
        }

    }

}

//节点类
class Node {
    int val;
    Node prev;
    Node next;

    public Node(int val) {
        this.val = val;
    }

    public Node(int val, Node prev, Node next) {
        this.val = val;
        this.prev = prev;
        this.next = next;
    }
}
 

顺序表 & 链表

顺序表:

优点:空间连续、支持随机访问

缺点:中间或前面部分的插入删除时间复杂度O(N)

增容的代价比较大。

链表:

优点:任意位置插入删除时间复杂度为O(1)

没有增容问题,插入一个开辟一个空间

缺点:以节点为单位存储,不支持随机访问

总结

到此这篇关于Java实现顺序表和链表结构的文章就介绍到这了,更多相关Java顺序表和链表内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JAVA模拟新增顺序表及单链表

    最近在回顾大学学的数据结构,这里给大家用java模拟顺序表和单链表的新增 1顺序表新增 /** * 顺序表 * * @author cjd * */ public class ArrayList { private Object[] elementData; // 底层是一个数组,目前还没有确定长度 private int size; // 不是数组分配了几个空间,而是元素的个数 public ArrayList() { this(4); } public ArrayList(int initi

  • Java数据结构之顺序表和链表精解

    目录 前言 1. 顺序表 代码实现 2. 链表 链表图解 代码实现 前言 两个数据结构:顺序表和链表 数据结构是一门学科,和语言无关. 数据 + 结构:一种描述和组织数据的方式. 1. 顺序表 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储.在数组上完成数据的增删查改.其逻辑上和物理上都是连续的. 问题引入:一个数组放在这,我们如何才能自己不去数,让程序自己进行计数? 答:在引入变量,每次放一个元素就更新一次.(如下图,为问题的示意) 也就是说顺序表的底层

  • Java实现顺序表和链表结构

    目录 前言: 顺序表 定义: 实现方法: 代码实现: 链表 定义: 分类: 实现方法: 代码实现: 顺序表 & 链表 总结 前言: 线性表(linear list)是n个具有相同特性的数据元素的有限序列. 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表.链表.栈.队列.字符串. 顺序表 定义: 用一段物理地址连续的存储单元依次存储数据元素的线性结构(逻辑上连续,物理上也连续) (1)静态顺序表:使用定长数组存储. (2)动态顺序表:使用动态开辟的数组存储 [注意]静态顺序表的定长数

  • Java全面讲解顺序表与链表的使用

    目录 线性表 顺序表 链表 小结 线性表 线性表 ( linear list ) 是 n 个具有相同特性的数据元素的有限序列. 线性表是一种在实际中广泛使用的数据结构,常见 的线性表:顺序表.链表.栈.队列.字符串 ... 线性表在逻辑上是线性结构,也就说是连续的一条直线.但是在物理结构(内存上)上并不一定是连续的,线性表在物理上存储时,通常以数组(在物理上是连续的)和链式结构(在物理上不连续)的形式存储. 顺序表 顺序表是用一段 物理地址连续 的存储单元依次存储数据元素的线性结构,一般情况下采

  • C语言编程数据结构线性表之顺序表和链表原理分析

    目录 线性表的定义和特点 线性结构的特点 线性表 顺序存储 顺序表的元素类型定义 顺序表的增删查改 初始化顺序表 扩容顺序表 尾插法增加元素 头插法 任意位置删除 任意位置添加 线性表的链式存储 数据域与指针域 初始化链表 尾插法增加链表结点 头插法添加链表结点 打印链表 任意位置的删除 双向链表 测试双向链表(主函数) 初始化双向链表 头插法插入元素 尾插法插入元素 尾删法删除结点 头删法删除结点 doubly-Linked list.c文件 doubly-Linkedlist.h 线性表的定

  • Java数据结构顺序表用法详解

    目录 1.什么是顺序表 2.顺序表的基本功能和结构 3.顺序表基本功能的实现和解析 1.判断线性表是否为空 2.获取指定位置的元素 3.向线性表表添加元素 4.在位置i处插入元素 5.删除指定位置的元素,并返回该元素 6.查找t第一次出现的位置 7.手动扩容方法 1.什么是顺序表 在程序中,经常需要将一组(通常是同为某个类型的)数据元素作为整体管理和使用,需要创建这种元素组,用变量记录它们,传进传出函数等.一组数据中包含的元素个数可能发生变化(可以增加或删除元素). 对于这种需求,最简单的解决方

  • Java数据结构顺序表从零基础到精通进阶

    目录 一.什么是线性表 二.顺序表 三.手撕顺序表 属性定义 构造方法 接口实现 确保顺序表空间 增加元素 打印顺序表 判断顺序表中是否包含某个元素 查找元素 获取 pos 位置的元素 将 pos 位置的元素值设为 value 删除第一次出现的关键字key 获取顺序表长度 清空顺序表 删除所有的key 一.什么是线性表 线性表是最基本.最简单.也是最常用的一种数据结构.线性表*(linear list)*是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列.常见的线性表有顺序表,链

  • Java数据结构顺序表的详细讲解

    目录 写在前面 1.线性表 2.顺序表的实现 2.1增加数据 2.1.1尾部增加数据 2.1.2任意位置增加数据 2.2查找数据 2.3删除数据 2.4修改数据 3.ArrayList 3.1ArrayList的实例化 3.2ArrayList常用的方法 写在前面 关于数据结构,Java官方其实已经帮我们写好并封装起来了,在真正需要使用的时候直接调用即可,但为了更好的理解数据结构,我会按照源码的思路写一个简化后的数据结构,默认接收的数据为int 1.线性表 线性表是多个具有相同特性的数据元素的序

  • Java实现顺序表的操作详解

    目录 一.顺序表是什么 二.自定义异常 空引用异常 下标越界异常 三.顺序表的方法 顺序表的实现 获取顺序表长度 顺序表是否为空 顺序表是否为满 打印顺序表 末尾新增元素 指定位置新增元素 判断是否包含某元素 查找某个元素对应的位置 获取 pos 位置的元素 给 pos 位置的元素赋值 删除第一次出现的关键字key 清空顺序表 四.自定义顺序表 一.顺序表是什么 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储.在数组上完成数据的增删查改. 数组不就是一个现

  • C语言顺序表的基本结构与实现思路详解

    目录 一.顺序表的概念与结构 1.线性表的解释 2.顺序表概念解释 二.顺序表的思路及代码实现详解 1.静态顺序表的实现 2.动态顺序表思路及代码实现 2.1 动态顺序表的整体思路 2.2 定义结构体的实现 2.3 初始化结构体 2.4 结构体打印 2.5 检查数组容量 2.6 头插 2.7 尾插 2.8 头删 2.9 尾删 2.10 任意删除 2.11 任意插入 2.12 空间释放 三.顺序表代码整合 SeqList.h SeqList.c test.c 一.顺序表的概念与结构 1.线性表的解

  • Java实现顺序表的增删查改功能

    创建顺序表 在java语言中要实现顺序表,首先创建一个类,因为顺序表本身就像数组,所以我们这里定义一个int类型的数组和usedata为有效数据,构造方法里先申请可以存放10个数据的空间. public class MyArraylist1 { public int[] elem;//存储数据的有效个数 public int usedata;//有效数据的个数 //构造方法 public MyArraylist1() { this.elem = new int[10]; } 主要实现以下方法 p

随机推荐