Bootstrap

链表在java和kotlin中的应用

上一篇中介绍了数组,数组的长度是不可变的,并且对象可以通过索引位置找到数据,因此查询效率比较高而增删效率比较低。这次介绍的链表增删效率高,查询效率低。下面介绍它的底层逻辑。

单向链表

 链表是由一个一个节点组成的,单向链表中每个节点分为两部分,一部分存放数据信息,另一部分存放下一个节点的地址。也就是说链表中相邻节点在物理上无序的,是通过前一个节点中的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)
}

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;