Java实现顺序表和链表结构

作者:?????* 时间:2023-11-13 09:35:43 

前言:

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

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

顺序表

定义:

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

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

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

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

实现方法:

增、删、改、查

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

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

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

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

Java实现顺序表和链表结构

代码实现:

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底层实现就是无头双向循环链表

实现方法:

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

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

Java实现顺序表和链表结构

Java实现顺序表和链表结构

Java实现顺序表和链表结构

代码实现:

(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)

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

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

来源:https://blog.csdn.net/m0_58652786/article/details/122863350

标签:Java,顺序表,链表
0
投稿

猜你喜欢

  • Java如何优雅替换if-else语句

    2023-02-23 10:30:27
  • Java数据结构之红黑树的真正理解

    2022-07-16 01:36:16
  • Spring Security登录表单配置示例详解

    2023-10-12 09:03:55
  • Java线程创建的四种方式总结

    2023-10-29 19:36:03
  • Android实现登录注册功能

    2023-07-31 09:35:44
  • C#设计模式之ChainOfResponsibility职责链模式解决真假美猴王问题实例

    2023-04-01 00:39:00
  • JAVA中的静态代理、动态代理以及CGLIB动态代理总结

    2023-04-03 14:33:39
  • C#使用Lambda表达式简化代码的示例详解

    2022-09-16 03:03:36
  • Android编程实现的一键锁屏程序详解

    2022-09-03 16:44:27
  • java.lang.Runtime.exec() Payload知识点详解

    2023-11-30 09:56:10
  • Android 6.0权限请求相关及权限分组方法

    2023-11-27 20:08:03
  • Springboot 整合 RabbitMQ 消息队列 详情

    2021-07-17 18:49:42
  • C# List<T> Contains<T>()的用法小结

    2021-05-29 11:44:56
  • JAVA版排序算法之快速排序示例

    2023-04-20 04:37:53
  • Java(TM) Platform SE binary 打开jar文件的操作

    2021-10-02 00:08:12
  • 手动添加jar包进Maven本地库内的方法

    2023-08-03 03:10:09
  • C#中SQL参数传入空值报错解决方案

    2023-12-14 14:28:59
  • selenium高效应对Web页面元素刷新的实例讲解

    2022-12-04 08:17:33
  • 详解C# partial 关键字的使用

    2023-06-07 20:46:09
  • Android 架构之数据库框架搭建

    2021-09-28 23:26:06
  • asp之家 软件编程 m.aspxhome.com