Data Structure : LinkedList

Why do we need LinkedList?


Java的Collection当中有ArrayList作为可变(表面上可变)长的数组,然而其底层也是利用数组实现的。所以,存在两个明显的短板。


1)当增加元素填满了底层数组的时候,底层数组需要resize,这时候需要创建一个1.5x长度的数组,然后复制旧数组的内容到新的数组,耗费许多时间;

2)当删除某个index的元素时,由于底层数组不能有hole,导致每次删除时都需要元素移位。同时,由于这个原因,ArrayList一旦remove某个index的元素后,之后的所有元素的坐标都会往前移。


那么LinkedList有什么好处呢?


1)空间效率:由于ArrayList的底层数组是一开始就创建好的,所以当没填满时,多余的空间是浪费的;而链表只需要在需要的时候重新创建多一个Node即可。

2)添加效率:没有resize的消耗;

3)删除效率:删除元素不需要移位。


LinkedList的坏处:


1)随机读取某个index的时候,必须要顺序读取其前面所有的节点。

2)每个存储单元(节点)需要额外的空间存储指向下一个节点的指针。


LinkedList的基础实现代码:


package datastruct.firehotest.net.csdn.blog;

import java.util.Iterator;
import java.util.NoSuchElementException;

public class LinkedListImp<AnyType> implements Iterable<AnyType> {
	/*only need to maintain a head*/
	private Node<AnyType> head;
	
	/*Inner class node*/
	private static class Node<AnyType> {
		public AnyType value;
		public Node<AnyType> next;
		
		public Node(AnyType input,Node<AnyType> n) {
			value = input;
			next = n;
		}
	}
	
	public LinkedListImp (){
		head = null;
	}
	
	/**
	 * implemented Iterable because it is a data structure
	 */
	@Override
	public LListIterator iterator() {
		return new LListIterator();
	}
	
	private class LListIterator implements Iterator<AnyType> {
		private Node<AnyType> nextNode;
		
		public LListIterator () {
			nextNode = head;
		}
		
		@Override
		public boolean hasNext() {
			return (nextNode != null);
		}
		
		@Override
		public AnyType next() {
			if (nextNode == null) {
				throw new NoSuchElementException();
			}
			
			AnyType returnVal = nextNode.value;
			nextNode = nextNode.next;
			
			return returnVal;
		}
	}
	/**
	 * Need to implement addFirst addLast insertAfter insertBefore remove
	 * 
	 */
	
	public void addFirst(AnyType ele) {
		head = new Node<AnyType>(ele,head);
	}
	
	public void addLast(AnyType ele) {
		if (head == null) {
			addFirst(ele);
		}
		
		Node<AnyType> tmp = this.head;
		
		while(tmp.next != null) {
			tmp = tmp.next;
		}
		
		tmp.next = new Node<AnyType>(ele,null);
	}
	
	public boolean insertAfter(AnyType key,AnyType ele) {
		if (head == null) {
			return false;
		}
		
		Node<AnyType> tmp = this.head;
		
		while(tmp != null && !(tmp.value.equals(key))) {
			tmp = tmp.next;
		}
		
		if (tmp == null) {
			return false;
		} 
		tmp.next = new Node<AnyType>(ele,tmp.next);
		return true;
	}
	
	public boolean insertBefore(AnyType key,AnyType ele) {
		if (head == null) {
			return false;
		}
		
		//这里漏了考虑,凡是涉及到前后的双指针的题目都需要考虑只有一个元素的极端情况
		if (head.value.equals(ele)) {
			addFirst(ele);
		}
		
		Node<AnyType> prev = null;
		Node<AnyType> cur = head;
		
		while(cur != null && !(cur.value.equals(key))){
			prev = cur;
			cur = cur.next;
		}
		
		if (cur == null) {
			return false;
		}
		
		prev.next = new Node<AnyType>(ele,prev.next);
		return true;
	}
	
	public boolean remove(AnyType ele) {
		if (head == null) {
			return false;
		}
		
		if (head.value.equals(ele)) {
			head = head.next;
			return true;
		}
		
		Node<AnyType> prev = null;
		Node<AnyType> cur = head;
		
		while(cur != null && !(cur.value.equals(ele))){
			prev = cur;
			cur = cur.next;
		}
		
		if (cur == null) {
			return false;
		}
		
		prev.next = cur.next;
		return true;
	}
	
	public static void main(String[] args) {
		
	}

}

实现的过程中有以下问题:


1)记得涉及的泛型的自定义类且非实现具备泛型的接口的自定义类,需要在声明的时候声明泛型。内部定义构造函数的时候不需要写上泛型的尖括号。但是,在类外用到这个类型的时候,包括通过new调用构造函数,时刻记得加上尖括号。


2)当这个自定义类的实现具有泛型的接口的时候,把尖括号放在继承的接口那里,而自定义类的类名在定义的时候或者外部调用的时候都不需要加尖括号。


3)当用到了prev 和 cur这种两个指针的题目,需要考虑链表只有一个元素的时候,prev是Null的情况。此时,需要处理这一extreme cases. 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis does not have a built-in data structure called LinkedList, but it does have a set of data structures that can be used to implement a LinkedList. One way to implement a LinkedList in Redis is to use a combination of List and Hash data structures. Each node in the LinkedList can be represented as a Hash, where the key is the node identifier and the value is a Hash containing the node data and the next node identifier. To insert a new node at the beginning of the LinkedList, we can use the LPUSH command to add the new node identifier to the front of the List, and then use the HSET command to set the data and next node identifier for the new node. To insert a new node at the end of the LinkedList, we can use the RPUSH command to add the new node identifier to the end of the List, and then use the HSET command to set the data and next node identifier for the new node. To traverse the LinkedList, we can start at the head of the List and use the HGETALL command to retrieve the data and next node identifier for each node in the LinkedList. Here is an example implementation of a LinkedList in Redis using Lists and Hashes: ``` # Insert a new node at the beginning of the LinkedList LPUSH mylist node1 HSET node1 data "hello" nextnode "node2" # Insert a new node at the end of the LinkedList RPUSH mylist node2 HSET node2 data "world" nextnode "node3" # Traverse the LinkedList $ head = "node1" $ while [ $head != "" ] $ do $ data=$(redis-cli HGETALL $head | awk '{print $1"="$2}') $ echo $data $ head=$(redis-cli HGET $head nextnode) $ done data=hello nextnode=node2 data=world nextnode=node3 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值