上一篇中介绍了数组,数组的长度是不可变的,并且对象可以通过索引位置找到数据,因此查询效率比较高而增删效率比较低。这次介绍的链表增删效率高,查询效率低。下面介绍它的底层逻辑。
单向链表
链表是由一个一个节点组成的,单向链表中每个节点分为两部分,一部分存放数据信息,另一部分存放下一个节点的地址。也就是说链表中相邻节点在物理上无序的,是通过前一个节点中的next,寻找下一个节点的地址。正是因为链表的无序性,它的查询效率低;又因为它是靠节点传递下一个节点的地址,它的增删效率较高。
单向链表的第一个节点称为首节点,一般来说头节点是不用来存放数据的,它是整个链表的起始。
双向链表
双向链表的每一个节点都有两个指针,next指向下一个节点的地址,pre指向前一个节点的地址。双向链表的第一个节点称为头节点,它没有上一个节点,pre中没有值;双向链表的最后一个节点称为尾结点,它没有下一个节点,next中没有值。通常来说头结点和尾结点中只是链表的两个入口,不存放数据。
相较于单向链表,双向链表可以从首尾两个方向进行查找,它的查询能力更强。
单向链表的代码
Java中单向链表
public class MyLinkedList {
//创建节点
class FruitNode {
int num;//序号
String fruitName;//存储的值
FruitNode next;//指向下一个节点
public FruitNode() {
this.fruitName = null;
}
//创建两个构造函数
public FruitNode(String fruitName, FruitNode next, int num) {
this.fruitName = fruitName;
this.next = next;
this.num = num;
}
public FruitNode(String fruitName, int num) {
this.fruitName = fruitName;
this.num = num;
}
}
//创建头节点,不保存值,不空即可
private FruitNode headNode = new FruitNode("111",-1);
private int size;
//获取链表长度
public int getSize() {
System.out.println(size);
return size;
}
public void setSize(int size) {
this.size = size;
}
//新建一个辅助节点,相当于指针的作用向后移动
public void add(FruitNode fruit) {
FruitNode temp = headNode;
//寻找最后一个节点
while(temp.next!=null) {
//指针向后移动
temp = temp.next;
}
temp.next = fruit;
size++;
}
//删除节点
public void delete(String fruitName) {
FruitNode temp = headNode;
while(temp.next!=null) {
if(temp.next.fruitName == fruitName) {
temp.next = temp.next.next;
break;
}
temp = temp.next;
}
size--;
}
//更改数据
public void update(String fruitName, int num) {
FruitNode temp = headNode;
while(temp.next!=null) {
if(temp.next.num == num) {
temp.next.fruitName = fruitName;
}
temp = temp.next;
}
}
//重写toString方法
public String toString() {
StringBuilder sb = new StringBuilder("[");
FruitNode temp = headNode.next;
while(temp != null) {
sb.append(temp.fruitName+",");
temp = temp.next;
}
sb.setCharAt(sb.length()-1, ']');
return sb.toString();
}
public static void main(String[] args) {
MyLinkedList list = new MyLinkedList();
//增加四个元素
list.add(list.new FruitNode("orange",1));
list.add(list.new FruitNode("apple",2));
list.add(list.new FruitNode("pear",3));
list.add(list.new FruitNode("melon",4));
System.out.println(list);
//删除元素
list.delete("pear");
System.out.println(list);
//将num为1的水果换为香蕉
list.update("banana", 1);
System.out.println(list);
//获取长度
list.getSize();
}
}
kotlin中的单向链表
package cas.igsnrr.tt
class MyLinkedList {
class FruitNode {
var num //序号
= 0
var fruitName //存储的值
: String?
var next //指向下一个节点
: FruitNode? = null
//创建两个构造函数
constructor(fruitName: String?, next: FruitNode?, num: Int) {
this.fruitName = fruitName
this.next = next
this.num = num
}
constructor(fruitName: String?, num: Int) {
this.fruitName = fruitName
this.num = num
}
}
//创建头节点,不保存值,不空即可
private val headNode = FruitNode("111", -1)
private var size = 0
//获取链表长度
fun getSize(): Int {
println(size)
return size
}
fun setSize(size: Int) {
this.size = size
}
//新建一个辅助节点,相当于指针的作用向后移动
fun add(fruit: FruitNode?) {
var temp: FruitNode? = headNode
//寻找最后一个节点
while (temp!!.next != null) {
//指针向后移动
temp = temp.next
}
temp.next = fruit
size++
}
//删除节点
fun delete(fruitName: String) {
var temp: FruitNode? = headNode
while (temp!!.next != null) {
if (temp.next!!.fruitName === fruitName) {
temp.next = temp.next!!.next
break
}
temp = temp.next
}
size--
}
//更改数据
fun update(fruitName: String?, num: Int) {
var temp: FruitNode? = headNode
while (temp!!.next != null) {
if (temp.next!!.num == num) {
temp.next!!.fruitName = fruitName
}
temp = temp.next
}
}
//重写toString方法
override fun toString(): String {
val sb = StringBuilder("[")
var temp = headNode.next
while (temp != null) {
sb.append(temp.fruitName + ",")
temp = temp.next
}
sb.setCharAt(sb.length - 1, ']')
return sb.toString()
}
}
fun main() {
val list = MyLinkedList().apply {
//增加四个元素
add(MyLinkedList.FruitNode("orange", 1))
add(MyLinkedList.FruitNode("apple", 2))
add(MyLinkedList.FruitNode("pear", 3))
add(MyLinkedList.FruitNode("melon", 4))
println(this)
//删除元素
delete("pear")
println(this)
//将num为1的水果换为香蕉
update("banana", 1)
println(this)
//获取长度
getSize()
}
}
双向链表的代码
Java中的双向链表
public class DoubleLinkedList {
//创建节点类
class FruitNode{
FruitNode pre;
FruitNode next;
String fruitName;
int num;
public FruitNode(String fruitName,
FruitNode pre, FruitNode next) {
this.fruitName = fruitName;
this.pre = pre;
this.next = next;
}
public FruitNode(String fruitName, int num) {
this.fruitName = fruitName;
}
public FruitNode() {}
}
//头结点和尾结点不存储数据
FruitNode head = new FruitNode("11",-1);
FruitNode tail = new FruitNode("11",-1);
public int size;
//获取节点
public FruitNode getNode(int no) {
FruitNode temp;
if(no<=(size>>1)) {
temp = head.next;
for(int i=1;i<no;i++) {
temp = temp.next;
}
}
else {
temp = tail.pre;
for(int i=size;i>no;i--) {
temp = temp.pre;
}
}
return temp;
}
public void add(FruitNode fruit) {
if(tail.pre == null) {
head.next = fruit;
fruit.pre = head;
tail.pre = fruit;
fruit.next = tail;
}else {
fruit.pre = tail.pre;
tail.pre.next = fruit;
fruit.next = tail;
tail.pre = fruit;
tail.next = null;
}
size++;
}
//双向遍历
public void delete(int no) {
FruitNode dd = getNode(no);
if(no<=(size>>1)) {
FruitNode temp = head;
for(int i=0;i<=no;i++) {
temp = temp.next;
if(temp.fruitName == dd.fruitName) {
temp.pre.next = temp.next;
temp.next.pre = temp.pre;
}
}
}else {
FruitNode temp = tail;
for(int i=size+1;i>no;i--) {
temp = temp.pre;
if(temp.fruitName == dd.fruitName) {
temp.pre.next = temp.next;
temp.next.pre = temp.pre;
}
}
}
size--;
}
public String toString() {
StringBuilder sb = new StringBuilder("[");
FruitNode temp = head.next;
while(temp.next != null) {
sb.append(temp.fruitName+",");
temp = temp.next;
}
sb.setCharAt(sb.length()-1, ']');
return sb.toString();
}
public static void main(String[] args) {
DoubleLinkedList list = new DoubleLinkedList();
list.add(list.new FruitNode("apple",1));
list.add(list.new FruitNode("pear",2));
list.add(list.new FruitNode("banana",3));
list.add(list.new FruitNode("melon",4));
list.add(list.new FruitNode("pineapple",5));
list.add(list.new FruitNode("peach",6));
list.add(list.new FruitNode("watermelon",7));
System.out.println(list.toString());
System.out.println(list.size);
list.delete(4);
System.out.println(list.toString());
}
}
kotlin中的双向链表
class DoubleLinkedList {
//创建节点类
inner class FruitNode {
var pre: FruitNode? = null
var next: FruitNode? = null
var fruitName: String? = null
var num = 0
constructor(
fruitName: String?,
pre: FruitNode?, next: FruitNode?
) {
this.fruitName = fruitName
this.pre = pre
this.next = next
}
constructor(fruitName: String?, num: Int) {
this.fruitName = fruitName
}
constructor() {}
}
//头结点和尾结点不存储数据
var head = FruitNode("11", -1)
var tail = FruitNode("11", -1)
var size = 0
//获取节点
fun getNode(no: Int): FruitNode? {
var temp: FruitNode?
if (no <= size shr 1) {
temp = head.next
for (i in 1 until no) {
temp = temp!!.next
}
} else {
temp = tail.pre
for (i in size downTo no + 1) {
temp = temp!!.pre
}
}
return temp
}
fun add(fruit: FruitNode) {
if (tail.pre == null) {
head.next = fruit
fruit.pre = head
tail.pre = fruit
fruit.next = tail
} else {
fruit.pre = tail.pre
tail.pre!!.next = fruit
fruit.next = tail
tail.pre = fruit
tail.next = null
}
size++
}
//双向遍历
fun delete(no: Int) {
val dd = getNode(no)
if (no <= size shr 1) {
var temp: FruitNode? = head
for (i in 0..no) {
temp = temp!!.next
if (temp!!.fruitName === dd!!.fruitName) {
temp!!.pre!!.next = temp.next
temp.next!!.pre = temp.pre
}
}
} else {
var temp: FruitNode? = tail
for (i in size + 1 downTo no + 1) {
temp = temp!!.pre
if (temp!!.fruitName === dd!!.fruitName) {
temp!!.pre!!.next = temp.next
temp.next!!.pre = temp.pre
}
}
}
size--
}
override fun toString(): String {
val sb = StringBuilder("[")
var temp = head.next
while (temp!!.next != null) {
sb.append(temp.fruitName + ",")
temp = temp.next
}
sb.setCharAt(sb.length - 1, ']')
return sb.toString()
}
}
fun main() {
val list = DoubleLinkedList()
list.add(list.FruitNode("apple", 1))
list.add(list.FruitNode("pear", 2))
list.add(list.FruitNode("banana", 3))
list.add(list.FruitNode("melon", 4))
list.add(list.FruitNode("pineapple", 5))
list.add(list.FruitNode("peach", 6))
list.add(list.FruitNode("watermelon", 7))
println(list.toString())
println(list.size)
list.delete(4)
println(list.toString())
println(list.size)
}