链表(java语言版)

链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。

  使用链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。

链表的基本单位是节点(Node),每个节点有数据域和指针域,数据域存储数据,指针域存储对下一个节点的引用。

链表根据特点又分为单向链表、双向链表、循环链表。

linkedList

单链表

单链表是最简单的链表,只有一个数据域和一个指针域。

001、单链表建立

建立链表需要有一个Node节点作为基础单位。

class Node{//结点
		private Object data;//每个节点的数据
		private Node next;//指向下一个节点
		          
		public Node(Object data){
		    this.data = data;
		    }
	}

加入一些基本的属性,方法就可以构成一个单链表。

public class SingleLinkList {
	
	private class Node{//结点
		private Object data;//每个节点的数据
		private Node next;//每个节点指向下一个节点的连接
		          
		public Node(Object data){
		    this.data = data;
		    }
	}
	
	private int size;//链表长度
	private Node head;//头结点
	
	public SingleLinkList() {//初始化
		size=0;
		head=null;
	}

	public int getSize() {//实际长度
		return size;
	}
    
    
	public boolean isEmpty(){//判空
		
		return this.getSize()==0;
	}
    
    private boolean isError(int index){//索引异常处理
		if(isEmpty()){
			System.out.println("空表");
			return true;
		}
		if(index<=0|index>=getSize()){
			System.out.println("索引出错");
			return true;
		}
		return false;
	}

002、加入元素

加入元素可以从头部加入,也可以从尾部加入。

public boolean addHead(Object obj) {//头插法增加元素
		Node node=new Node(obj);
		if(this.getSize()==0) {
			head=node;
		}else {
			node.next=head;
			head=node;
		}
		size++;
		System.out.println("从头部添加成功");
		return true;
	}

	public boolean add(Object obj){//尾插法
		Node node=new Node(obj);
		if(this.getSize()==0) {
			head=node;
		}else {
			Node p=head;
			while(p.next!=null){
				p=p.next;
			}
			p.next=node;
		}
		size++;
		System.out.println("从尾部添加成功");
		return true;
	}

003、插入

通过索引插入元素。

public void insert(int index,Object e){//插入元素
		if(this.isError(index)){
			return ;
		}

		Node p=head;
		for(int i=1;i<index-1;i++){
			p=p.next;
		}
		Node f=new Node(e);
		Node s=p.next;
		f.next=s;
		p.next=f;
		size++;
	}

004、查询

get方法通过索引得到链表中的元素, contain方法查询元素是否在表中,若在,返回索引,不在,返回-1。

	public int contain(Object e){//查询元素是否在表中,若在,返回索引,不在,返回-1
		if(isEmpty()){
			System.out.println("空表");
			return -1;
		}
		Node p=head;
		for(int i=1;i<=getSize();i++){
			if(e==p.data){
				return i;
			}
			p=p.next;
		}
		return -1;
	}

	public Object get(int index){//按照索引得到元素
		if(this.isError(index)){
			return null;
		}
		Node p=head;
		for(int i=1;i<index;i++){
			p=p.next;
		}
		return p.data;
	}
}

005、删除

通过索引删除元素。

public void remove(int index){//删除元素del
		if(this.isError(index)){
			return ;
		}
		if(index==1){
			removeHead();
			return ;
		}

		Node p=head;
		for(int i=1;i<index-1;i++){
			p=p.next;
		}
		p.next=(p.next).next;
		size--;
	}

	public void removeHead(){
		head=head.next;
		size--;
	}

006、修改

修改元素,或者说更新元素。

public void replace(int index ,Object e){//修改元素update,set

		if(this.isError(index)){
			return ;
		}

		Node p=head;
		for(int i=1;i<index;i++){
			p=p.next;
		}
		p.data=e;
	}

007、遍历

遍历元素。

public void display(){//遍历
		if(this.isEmpty()){
			System.out.println("空表");
			return ;
		}
		
		System.out.println("开始遍历数据:");
		Node p=head;
		while(p!=null){
			System.out.print("  "+p.data);
			p=p.next;
		}
		System.out.println("\n遍历结束");
	}
	

以上链表提供了基本的增删改查功能,接下来测试一下。

008、测试

因为本链表的数据类型被定义为Object类型,所以可以装载各种数据,包括对象。

先测试基本的增加元素和遍历功能。

public static void main(String[] args) {
		SingleLinkList s=new SingleLinkList();
		s.addHead("ok");
		s.addHead("world");
		s.addHead("Hello");
		s.addHead(1);
		s.add("yes");
		s.add("no");
		People p=new People("Tom",16);//一个对象
		s.add(p);
		s.display();

	}

如下,所有数据正常。

从头部添加成功
从头部添加成功
从头部添加成功
从头部添加成功
从尾部添加成功
从尾部添加成功
从尾部添加成功
开始遍历数据:
  1  Hello  world  ok  yes  no  { name='Tom', age='16'}
遍历结束

在以上数据下继续测试:

		int index=2;
		Object o=s.get(index);
		System.out.println("第"+index+"号元素是"+o);

		Object e="yes";
		Object i=s.contain(e);
		System.out.println(e+"在容器中是第"+i+"号元素");

		Object e1="full";
		s.insert(index,e1);
		System.out.printf("在%d处插入数据%s\n",index,e1);
		s.display();

		s.remove(index);
		System.out.println("移除数据"+index);
		s.display();

		s.replace(index, "go");
		System.out.println("修改数据"+index);
		s.display();

结果如下:

第2号元素是Hello
yes在容器中是第5号元素
在2处插入数据full
开始遍历数据:
  1  full  Hello  world  ok  yes  no  { name='Tom', age='16'}
遍历结束
移除数据2
开始遍历数据:
  1  Hello  world  ok  yes  no  { name='Tom', age='16'}
遍历结束
修改数据2
开始遍历数据:
  1  go  world  ok  yes  no  { name='Tom', age='16'}
遍历结束

循环链表和双链表

理解了单链表基本也就理解了链表,双向链表相比单向链表就多了一个指针域,用于指向前一个节点。

class Node{//结点
		private Object data;//每个节点的数据
		private Node next;//指向后一个节点
    	private Node prev;//指向前一个节点
		          
		public Node(Object data){
		    this.data = data;
		    }
	}

链表的尾节点如果可以指向头节点,那么这个链表就是循环链表。

双向循环链表由于可以从任意节点索引到整个链表的数据,兼具其他链表的优点,经常被计算机语言所采用。

如java中内置的集合类LinkedList.

关于循环链表和双向链表可以仿照单向链表自己实现一下,在具体细节的处理上有些许差别。

posted @ 2024-04-11 11:40  cgl_dong  阅读(26)  评论(0编辑  收藏  举报