JAVA数据结构——链表

1.我们知道数组是连续存储,这样一来会大大降低内存使用率,而如果采用链表的话,由于链表是采用分散存储,这样就大大提升了内存使用率,而且在数组中的元素叫做元素,而在链表中的元素叫做节点。而元素和节点的差距就在于——节点不仅包括节点内容,还包括了下一个节点的地址。如图所示

image.png

2.那么我想手搓一个链表的话,就直接看代码吧。

 

csharp

复制代码

package com.example.main; //首先创建一个链表类。 public class Node { private int content; private Node next; public Node(int content, Node next) { this.content = content; this.next = next; } public int getContent() { return content; } public void setContent(int content) { this.content = content; } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } }

以下就是主方法创建一个包含9,2,4三个数字的链表,并且返回链表中的第一个节点。

 

php

复制代码

package com.example.main; public class Main { public static Node CreateLinkedList(int[] array){ //首先链表的根节点肯定是空的,所以先创建一个根节点。 Node root = null; //然后就是从末尾元素依次开始创建Node节点。 for (int i = array.length - 1; i >= 0; i--) { Node node = new Node(array[i],null); //将链表连接。 node.setNext(root); root = node; } return root; } public static void main(String[] args) { int[] array = new int[]{9,2,4}; //打印第一个链表的节点。 System.out.println(CreateLinkedList(array).getContent()); //打印第二个,可以以此类推。 System.out.println(CreateLinkedList(array).getNext().getContent()); } }

3.我们创建了链表后呢,就该实现链表的一些基本功能了,最常见的肯定就是读取查找功能了。直接看代码吧:

 

ini

复制代码

package com.example.main; public class Main { Node root = null; int size = 0; public Main(int[] array){ this.root = CreateLinkedList(array); this.size = array.length; } public static Node CreateLinkedList(int[] array){ //首先链表的根节点肯定是空的,所以先创建一个根节点。 Node root = null; //然后就是从末尾元素依次开始创建Node节点。 for (int i = array.length - 1; i >= 0; i--) { Node node = new Node(array[i],null); //将链表连接。 node.setNext(root); root = node; } return root; } //获取长度。 public int getSize(){ return this.size; } //获取某个索引节点内容 public int getContent(int index){ Node node = this.root; for (int i = 0; i < index; i++) { node = node.getNext(); } return node.getContent(); } //查找某个值的索引值,默认不存在为-1。 public int getIndex(int content){ if (this.root == null){ return -1; } Node node = this.root; int index = 0; while (index <= this.size - 1){ if (node.getContent() == content){ return index; } node = node.getNext(); index++; } return -1; } public static void main(String[] args) { int[] array = {9, 2, 4}; Main linkedList = new Main(array); System.out.println(linkedList.getIndex(2)); System.out.println(linkedList.getIndex(5)); System.out.println(linkedList.getContent(2)); } }

4.进阶还要有一些插入删除功能,废话不多说,咱们直接看代码:

 

ini

复制代码

package com.example.main; public class Main { private Node root = null; int size = 0; public Main(){} public Main(int[] array){ this.root = CreateLinkedList(array); this.size = array.length; } public static Node CreateLinkedList(int[] array){ //首先链表的根节点肯定是空的,所以先创建一个根节点。 Node root = null; //然后就是从末尾元素依次开始创建Node节点。 for (int i = array.length - 1; i >= 0; i--) { Node node = new Node(array[i],null); //将链表连接。 node.setNext(root); root = node; } return root; } //获取长度。 public int getSize(){ return this.size; } //获取某个索引节点内容 public int getContent(int index){ Node node = this.root; for (int i = 0; i < index; i++) { node = node.getNext(); } return node.getContent(); } //查找某个值的索引值,默认不存在为-1。 public int getIndex(int content){ if (this.root == null){ return -1; } Node node = this.root; int index = 0; while (index <= this.size - 1){ if (node.getContent() == content){ return index; } node = node.getNext(); index++; } return -1; } public boolean addLast(int value){ return add(value,this.size - 1); } //索引值为-1则代表在头部插入节点。 public boolean addFirst(int value){ return add(value,-1); } /*如果索引值为-1,则表示在头部添加节点。 如果索引值为0,则代表在第一位元节点后面添加。 而如果为n,则代表在第n位节点后面添加。 */ public boolean add(int value,int index){ if (index < -1 || index > this.size - 1){ return false; } else if (index == -1) { if (this.root == null){ this.root = new Node(value,null); } else { this.root = new Node(value,this.root); } } else { Node node = this.root; while (index > 0){ if (node.getNext() == null) { return false; } node = node.getNext(); index--; } if (node.getNext() == null){ node.setNext(new Node(value,null)); } else { Node newNode = new Node(value,node.getNext()); node.setNext(newNode); } } this.size++; return true; } //删除最后一个元素 public boolean removeLast(){ return remove(this.size - 1); } //删除第一个元素 public boolean removeFirst(){ return remove(0); } public Node getRoot() { return root; } public void setRoot(Node root) { this.root = root; } /* index值为0时,则代表要删除第1个链表节点, index值为n时,则代表要删除第n+1个链表节点。 */ public boolean remove(int index){ if (index < 0 || index > this.size - 1 || this.root == null){ return false; } else if (index == 0) { Node node = this.root; this.root = node.getNext(); node.setNext(null); } else { Node node = this.root; while (index > 1){ if (node.getNext() == null){ return false; } node = node.getNext(); index--; } if (node.getNext() == null){ return false; } Node newNode = node.getNext(); node.setNext(node.getNext().getNext()); newNode.setNext(null); } this.size--; return true; } //此处重写toString方法就是为了能够在控制台打印出链表节点内容。 @Override public String toString(){ if (this.root == null) { return ""; } Node node = this.root; StringBuilder builder = new StringBuilder(); while (node != null) { builder.append(node.getContent()).append(" "); node = node.getNext(); } return builder.toString(); } public static void main(String[] args) { Main main = new Main(); boolean result = main.addFirst(9); boolean bag = main.addLast(2); boolean re = main.remove(1); System.out.println(main.toString()); } }

这里有个知识点大家必须知道,就是我在里面重写toString方法首先String类是final修饰的终极类,因此它不是基本数据类型,所以不能够修改,但是StringBuffer类和StringBuilder类可以修改或添加删除元素,但是前者是线程安全的,后者不是。因此,如果想要多线程安全的修改字符串,那么应该使用前者,而如果要单线程高效率的修改则需要调用后者。

这里的add()方法和remove()方法一些Java入门小白可能看的有点懵,我并没有修改this.root,那么这个插入还有用吗?其实,这里运用了JVM的栈堆知识,因为this.root是Node类型,即是一个对象,因此它的地址存放在栈里面,而内容存放在堆里面。我们创建一个Node类型的变量node,它和this.root指向的是同一个在堆里面的对象,即共用同一个对象。

5.那么编写完,我们需要知道我们写的是单向链表,这种是最简单的,但是jdk里面就已经提供了现成的链表API,那就是LinkedList类,它和ArrayList类十分相似,前者的增加和删除效率强于后者,但是查找和修改效率要弱于后者。另外,LinkedList是双向链表。直接看代码吧:

6.经过了上面的编写,大家应该能看出链表数组之间的区别了,直接看图吧:

image.png

从上面的图可以明显地看出,只要是在频繁插入和删除的情况下,链表明显更优于数组。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值