(数据结构)线性表之链表,java实现

上一篇文章介绍了线性表中的顺序表,今天介绍线性表的链表。

链式存储结构的特点

顺序存储结构,是在内存中分配一组地址连续的地址来存储线性表中的元素,需要一次性分配连续的一片空间,链表采用一组地址任意的存储单元存放线性表中的数据元素,所以需要在每个数据元素中保存一个引用下一个元素的引用。

对比线性表的两种实现

 hk

顺序表

链表

 

空间

需要在内存中静态分配一组地址连续的内存,数组长度固定,所以总有一些数组元素被浪费

存储地址动态分布,空间不会被浪费,但需要一些空间来保存下一个或上一个结点的指针,所以也要牺牲一部分空间

 

时间

顺序表中的逻辑顺序与物理存储顺序相同,所以查找,读取性能很好,插入删除时,需要移动结点之后的元素,数组扩容时,需要复制数组元素,所以插入删除性能较差。

链表采用链式结构保存表内的元素,插入删除性能很好,查找性能不如顺序表

 

1.单链表的简单实现:
package xxx.hk.linearlist;

public class LinkList<T> {
	// 链表的长度
	private int size = 0;
	// 链表的头部
	private Node header;
	// 链表的尾部
	private Node tail;

	// 链表结点
	private class Node {
		// 结点的值
		private T data;
		// 下一个结点
		private Node next;

		public Node(T data, Node next) {
			super();
			this.data = data;
			this.next = next;
		}
	}

	public LinkList() {
		header = null;
		tail = null;
	}

	public LinkList(T data) {
		header = new Node(data, null);
		tail = header;
		size = 1;
	}

	/**
	 * 根据索引值返回结点
	 * 
	 * @param index
	 * @return 该索引的结点
	 */
	public Node getNodeByIndex(int index) {
		if (index < 0 || index > size - 1) {
			throw new IndexOutOfBoundsException("下标越界异常");
		}
		Node currentNode = header;
		for (int i = 0; i < size && header != null; i++, currentNode = currentNode.next) {
			if (i == index) {
				return currentNode;
			}
		}
		return null;
	}

	/**
	 * 根据索引值返回结点的值
	 * 
	 * @param index
	 * @return 结点的值
	 */
	public T get(int index) {
		return getNodeByIndex(index).data;
	}

	/**
	 * 尾插法插入结点
	 * 
	 * @param data
	 */
	public void add(T data) {
		Node node = new Node(data, null);
		// 如果链表为空
		if (header == null) {
			header = node;
			tail = header;
		} else {
			tail.next = node;
			tail = node;
		}
		size++;
	}

	/**
	 * 头插法插入结点
	 * 
	 * @param data
	 */
	public void addHead(T data) {
		Node node = new Node(data, null);
		// 如果链表为空
		if (tail == null) {
			header = node;
			tail = header;
		} else {
			node.next = header;
			header = node;
		}
		size++;
	}

	/**
	 * 插入到索引处
	 * @param data
	 * @param index
	 */
	public void insert(T data, int index) {
		// 插入位置小于0 插入到第一个
		if (index <= 0) {
			addHead(data);
			// 插入位置大于链表长度或者链表为空,插入到最后一个
		} else if (index >= size) {
			add(data);
		} else {
				//得到该索引前一个结点
				Node preNode = getNodeByIndex(index-1);	
				//修改next所指的结点
				preNode.next = new Node(data, preNode.next);
				size++;
			}
	}
	
	/**
	 * 删除索引处的结点
	 * @param index
	 * @return 删除结点的值
	 */
	public T deleteByIndex(int index){
		if(index<0||index>size){
			throw new IndexOutOfBoundsException("下标越界异常");
		}
		Node delNode = null;
		//如果删除头结点
		if(index==0){
			delNode = header;
			header = header.next;
		}else{
			Node preNode = getNodeByIndex(index-1);
			delNode = preNode.next;
			preNode.next = delNode.next;
		}
		size--;
		return delNode.data;
	}

	/**
	 * 删除最后一个
	 * @return
	 */
	public T delete(){
			return deleteByIndex(size-1);
	}
	
	/**
	 * 根据值找到索引值
	 * @param data
	 * @return 索引值 为找到则为-1
	 */
	public int getIndex(T data){
		if(header==null){
			return -1;
		}
		Node current = header;
		for(int i=0;i<size&¤t!=null;i++,current=current.next){
			if(current.data.equals(data)){
				return i;
			}
		}
		return -1;
	}
	
	/**
	 * 是否为空
	 * @return true/false
	 */
	public boolean isEmpty(){
		return size==0;
	}
	
	/**
	 * 清空链表
	 */
	public void clear(){
		header = null;
		tail = null;
		size = 0;
	}
	
	
	/**
	 * 链表的长度
	 * @return
	 */
	public int length(){
		return size;
	}
	
	/* 
	 * 重写的tostring方法
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString(){
		if(isEmpty()){
			return "[]";
		}else{
			StringBuilder sb = new StringBuilder("[");
			for(Node current = header;current!=null;current = current.next){
				sb.append(current.data+",");
			}
			int len = sb.length();
			return sb.delete(len-1,len).append("]").toString();
		}
	}
	
	//测试
	public static void main(String[] args) {
		LinkList<String> list = new LinkList<>();
		list.add("hk");
		System.out.println(list.length());
		System.out.println(list);
		
		list.add("wyh");
		list.add("123");
		System.out.println(list.length());
		System.out.println(list);
		
		System.out.println(list.isEmpty());
		list.insert("aa", 1);
		System.out.println(list);
		list.delete();
		System.out.println(list);
		list.deleteByIndex(1);
		System.out.println(list);
		System.out.println(list.getIndex("hk"));
		list.clear();
		System.out.println(list);
	}
}

2.循环链表:尾结点的下一结点指针指向头结点
3.双向链表:每个元素结点包含 上一个结点和下一个结点的引用
package xxx.hk.linearlist;

public class DuLinkList<T> {
	// 链表长度
	private int length;
	// 头结点
	private Node header;
	// 尾结点
	private Node tail;

	// 结点内部类
	private class Node {
		// 结点的值
		T data;
		// 上一个结点
		Node prev;
		// 下一个结点
		Node next;

		public Node(T data, DuLinkList<T>.Node prev, DuLinkList<T>.Node next) {
			this.data = data;
			this.prev = prev;
			this.next = next;
		}
	}

	public DuLinkList() {
		header = null;
		tail = null;
	}

	public DuLinkList(T data) {
		header = new Node(data, null, null);
		tail = header;
		length = 1;
	}

	public DuLinkList(T data1, T data2, Node head, Node tail) {
		header = new Node(data1, null, tail);
		tail = new Node(data2, header, null);
		length = 2;
	}

	/**
	 * 尾插法插入结点
	 * 
	 * @param data
	 */
	public void add(T data) {
		// 链表为空
		if (header == null) {
			header = new Node(data, null, null);
			tail = header;
			length++;
		} else {
			DuLinkList<T>.Node node = new Node(data, tail, null);
			tail.next = node;
			tail = node;
			length++;
		}
	}

	/**
	 * 头插法插入结点
	 * 
	 * @param data
	 */
	public void addAtHead(T data) {
		if (header == null) {
			header = new Node(data, null, null);
			tail = header;
			length++;
		} else {
			DuLinkList<T>.Node node = new Node(data, null, header);
			header.prev = node;
			header = node;
			length++;
		}
	}

	/**
	 * 根据索引值返回结点
	 * 
	 * @param index
	 * @return 索引值对应的结点
	 */
	public Node get(int index) {
		if (index < 0 || index > length - 1) {
			throw new IndexOutOfBoundsException("下标越界");
		}
		// 从链首开始找
		if (index <= length / 2) {
			Node current = header;
			for (int i = 0; i < length && current != null; i++, current = current.next) {
				if (index == i) {
					return current;
				}
			}
		} else { // 从链尾开始找
			Node current = tail;
			for (int i = length - 1; i >= 0 && current != null; i--, current = current.prev) {
				if (index == i) {
					return current;
				}
			}
		}
		return null;
	}

	/**
	 * 在指定位置插入元素结点
	 * 
	 * @param data
	 * @param index
	 */
	public void addAtIndex(T data, int index) {
		// 插入到链首
		if (index == 0) {
			addAtHead(data);
			// 插入到链尾
		} else if (index == length) {
			add(data);
		} else {
			Node node = get(index);
			Node newNode = new Node(data, node.prev, node);
			node.prev = newNode;
			length++;
		}
	}

	/**
	 * 删除指定位置的结点
	 * 
	 * @param index
	 * @return 删除结点的值
	 */
	public T delAtIndex(int index) {
		if (index == 0) {
			return removeFirst();
		} else if (index == length - 1) {
			return remove();
		} else {
			Node node = get(index);
			T data = node.data;
			node.prev.next = node.next;
			node.next.prev = node.prev;
			node = null;
			length--;
			return data;
		}
	}

	/**
	 * 删除最后一个
	 * 
	 * @return 删除结点的值
	 */
	public T remove() {
		// 如果链表不为空
		if (tail != null) {
			T data = tail.data;
			tail = tail.prev;
			tail.next = null;
			length--;
			return data;
		}
		return null;
	}

	/**
	 * 删除第一个
	 * 
	 * @return 删除结点的值
	 */
	public T removeFirst() {
		// 如果链表不为空
		if (header != null) {
			T data = header.data;
			header = header.next;
			header.prev = null;
			length--;
			return data;
		}
		return null;
	}

	/**
	 * 判断链表是否为空
	 * @return true/false
	 */
	public boolean isEmpty(){
		return length>0?false:true;
	}
	
	//返回链表的长度
	public int length(){
		return length;
	}
	
	/* 
	 * 打印链表
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		if(isEmpty()){
			return "[]";
		}else{
			StringBuilder sb = new StringBuilder("[");
			Node current = header;
			for(int i = 0;i<length&¤t!=null;i++,current=current.next){
				sb.append(current.data+",");
			}
			//删除最后一个,
			sb.append("]").delete(sb.length()-2, sb.length()-1);
			return sb.toString();
		}
	}
	
	/**
	 * 倒叙打印链表
	 * @return String 链表的所有节点的值s
	 */
	public String reverseToString(){
		if(isEmpty()){
			return "[]";
		}else{
			StringBuilder sb = new StringBuilder("[");
			Node current = tail;
			for(int i = length;i>0&¤t!=null;i++,current=current.prev){
				sb.append(current.data+",");
			}
			//删除最后一个,
			sb.append("]").delete(sb.length()-2, sb.length()-1);
			return sb.toString();
		}
	}
	
	public static void main(String[] args) {
		DuLinkList<Integer> duLinkList = new DuLinkList<>();
		/*System.out.println(duLinkList.length);
		System.out.println(duLinkList.isEmpty());
		*/
		duLinkList.add(1);
		/*System.out.println(duLinkList.length);
		System.out.println(duLinkList.isEmpty());*/
		duLinkList.add(2);
		duLinkList.addAtHead(0);
		duLinkList.addAtIndex(-1, 0);
		duLinkList.addAtIndex(3,4);
		
		//duLinkList.remove();
		//duLinkList.delAtIndex(0);
		duLinkList.delAtIndex(4);
		System.out.println(duLinkList.toString());
		System.out.println(duLinkList.reverseToString());
		
	}
	
}



顺序表、链表分别对应java中的 ArrayList、LinkedList的实现,可以参考源代码进行模仿改进。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值