java数据结构——1.单向链表

本篇中的引用是为了引起注意,不是引用

链表是一种线性表,不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针。

 

优点:使用链表结构可以不必要预先知道表的长度(大小),可以无限添加,可以实现灵活的内存动态管理,修改操作很简单。

缺点:查找困难,每一次查找都需要从头节点开始(或尾节点,这里由于是单向链表,所以只能从头节点开始遍历),且由于每一个节点都占用一块内存,所以内存开销较大。

单向链表采用下图方法进行存储:

也就是说,next是一个Node型数据,他就是下一个节点(指向下一个节点)

而我们要遍历,就从第一个开始,一直next,next,知道next为null时就遍历到头了。

若是我们要插入节点:insert(Node node, int index),首先要考虑是在哪里添加节点,如果我们要往头部之前添加,那么我们重新定义一个Head为要添加的节点,然后将新的Head的next赋值为之前的Head就可以了。如果我们要往中间添加节点,那么直接改变添加的位置之前的节点的next的赋值即可。上图:

这里可能有人会问,如果往尾部插入节点不用特殊考虑吗,答案是不用,因为当前尾节点指向null,我们把他当成普通节点处理,那么他会指向要插入的节点,而由于它的next为null,所以会导致新插入的节点的next指向null,没有特殊情况。

移除结点:remove(Node node),也是相同的道理,找到相应的前后节点,改变指针指向即可

添加节点:add(Node node),则直接遍历链表找到尾节点,然后将尾节点的next只想要添加的节点即可

根据下标查找结点:get(Int index),用来直接找到对应下标位置的节点,这个方法可以直接用在:插入,移除,添加以及自己扩展的方法中,只要需要获取相关的下表,都可以使用调用这个方法

 打印链表获取长度很简单,直接代码给出

下面是代码干货,注释很详细,可以直接阅读:

可以自己扩充方法。

Node.java:

public class Node {
	// 存放数据
	private Object data;
	// 存放下一个节点
	private Node nextNode;
	
	// 构造方法
	public Node(Object data, Node nextNode) {
		this.data = data;
		this.nextNode = nextNode;
	}
	public Node(Object data) {
		this.data = data;
	}
	
	// setter getter
	public Object getData() {
		return data;
	}
	public void setData(Object data) {
		this.data = data;
	}
	public Node getNextNode() {
		return nextNode;
	}
	public void setNextNode(Node nextNode) {
		this.nextNode = nextNode;
	}
	
	// 追加节点
	public void append(Node node) {
		if (this.nextNode == null) {      // 如果next为null,则将next指向传进来的node
			this.nextNode = node;
		}else { 
			this.nextNode.append(node);   // 如果next已经存在,则调用next节点的append方法推进
		}
	}

	// 打印节点信息
	public String toString() {
		return "Node [data=" + data + "]";
	}
	
}

LinkedList.java

public class LinkedList {
	// 头节点
	private Node head;
	// 节点数
	private int count = 0;
	
	// 添加节点
	public void add(Node node) {
		if (this.head == null) {      // 如果没有头节点,则添加的节点为头节点
			this.head = node;
			count++;
		}else {
			head.append(node);        // 如果有头节点,则让头节点处理追加节点
			count++;
		}
	}
	
	// 根据下标查找结点
	public Node get(int index) {
		if (index < 0 || index > count-1) {
			throw new RuntimeException("输入非法");
		}
		/*
		 * 这段代码去掉不会影响内容,因为下面new的Node已经将head赋值给它
		// 如果index是0,返回头节点
		if (index == 0) {
			return this.head;
		}
		*/    
		Node n = this.head;              
		for (int i=0; i<index; i++) {        //顺着头节点,根据传进来的位置,一直摸到要找的节点
			n = n.getNextNode();
		}
		return n;
	}
	
	// 查找节点是否在链里
	public boolean contains(Node node) {
		// 链表长度为0
		if (count == 0) {       
			return false;
		}
		Node n = head;
		// 对比data,不相同就查找下一个节点
		/*
		 * 写法1,通过count判定
		for (int i=0; i<count; i++) {
			if (n.getData().equals(node.getData())) {
				return true;
			}
			n = n.getNextNode();
		}
		*/
		// 写法2,通过next是否为空判定
		do {
			if (n.getData().equals(node.getData())) {
				return true;
			}
		}while((n = n.getNextNode()) != null);      // 移动到下个节点的位置,若该节点(下个节点)为空则跳出循环
		
		return false;
	}
	
	// 从链中移除节点
	// 关注头节点
	public void remove(int index) {
		if (index < 0 || index > count-1) {
			throw new RuntimeException("输入非法");
		}
		
		if (index == 0) {
			this.head = this.head.getNextNode();
			count--;
		}else {
			 //找到要删除节点的前一个节点
			Node n = this.get(index-1); 
			
			//跳过要删除的节点指向要删除节点的下一个节点
			n.setNextNode(n.getNextNode().getNextNode());    
			count--;
		}
	}
	
	// 插入节点
	public void insert(Node node, int index) {
		
		if (index < 0 || index > count) {
			throw new RuntimeException("位置有误,无法插入节点");
		}
		
		// 插入头节点
		if (index == 0) {
			node.setNextNode(head);               // 将插入节点的next指向当前的head头节点
			this.head = node;
			count++;
		}else {                                   // 插入到中间位置
			Node n = this.get(index-1);           // 找到要插入节点位置的前节点
			n.setNextNode(node);                  // 将前节点的next指向插入的节点
			node.setNextNode(n.getNextNode());    //将插入的节点的next指向后节点
			count++;
		}
		
	}
	
	// 打印所有节点
	public void printAllNode() {
		System.out.print("节点——");
		Node n = head;
		for (int i=0; i<count; i++) {
			System.out.print( " data:" + n.getData() + "->");
			n = n.getNextNode();
		}
		System.out.println();
	}
	
	// 链表长度
	public int length() {
		return count;
	}
}

Test.java

public class Test {
	public static void main(String[] args) {
		LinkedList list = new LinkedList();
		
		list.add(new Node("一"));
		list.add(new Node("二"));
		list.add(new Node("三"));
		
		System.out.println(list.get(0).toString());
		System.out.println(list.get(1).toString());
		System.out.println(list.get(2).toString());
		
		list.printAllNode();
		
		System.out.println(list.contains(new Node("一")));
		System.out.println(list.contains(new Node("二")));
		System.out.println(list.contains(new Node("三")));
		System.out.println(list.contains(new Node("四")));
		
		list.remove(0);
		
		list.printAllNode();
		
		list.remove(1);
		
		list.printAllNode();
		
		list.insert(new Node("一"), 0);
		
		list.printAllNode();
		
		//list.insert(new Node("三"), 3);      // 这里会报错:位置有误,无法插入节点
		list.insert(new Node("三"), 2);
		
		list.printAllNode();
	}
}

测试结果:

Node [data=一]
Node [data=二]
Node [data=三]
节点—— data:一-> data:二-> data:三->
true
true
true
false
节点—— data:二-> data:三->
节点—— data:二->
节点—— data:一-> data:二->
节点—— data:一-> data:二-> data:三->

如有不正确之处还请大佬指正,谢谢。 

猜你喜欢

转载自blog.csdn.net/qs395517243/article/details/81590748
今日推荐