链表及其基本操作(Java语言版)

一、链表

链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。

1.单向链表

单向链表,它包含两个域,一个信息域和一个指针域。信息域用来存储链表中要存放的值,指针域则指向下一个节点。这个链接指向列表中的下一个节点,而最后一个节点则指向一个空值。其结构图如下:
在这里插入图片描述

2、双向链表

双向链表在单链表的基础上,每个节点新增了一个指针域,用来指向前一个节点。其结构图如下:
在这里插入图片描述

二、链表的基本操作

首先创建一个LinkedList类。

0.链表节点的对象

创建一个ListNode类,包含两个属性,一个为链表节点的值,另一个为下一个节点的地址。
创建size属性记录链表长度

public class LinkedList {

	//定义节点
	static class ListNode {
		int val; // 链表存储的值
		ListNode next; // 指针,指向下一个节点
		ListNode() {
			
		}
		ListNode(int val) { 
			this.val = val; 
		}
		ListNode(int val, ListNode next) { 
			this.val = val;
			this.next = next; 
		}
	}
	
	int size; // 链表长度
	ListNode headNode; //链表头结点
	ListNode dummyHead = new ListNode(0);
}

1.头插法

头插法,即在链表首部添加一个节点作为新的头结点。
首先要新建一个ListNode 对象作为要插入的节点,然后根据情况进行操作。如果当前链表为空链表,则将该节点直接作为头结点即可创建一个链表;如果当前链表不是空链表,则将要插入的节点的next指针指向原本的头节点。

	public void headInsertion(int val) {
		ListNode newListNode = new ListNode(); // 新建一个对象,即为要插入的节点
		newListNode.val = val; // 对节点存入要插入的值
		if (headNode == null) { //头结点为空即是个空链表,头结点直接指向要插入的节点即可
			headNode = newListNode;
		} else {
			newListNode.next = headNode; // 要插入的节点应放在第一个,所以它的下一个节点指向head
			headNode = newListNode; // 插入后新插入的节点是新的头结点
		}
		size ++;
	}

2.任意位置插入节点

insert方法为在任意位置插入节点,参数中的index为插入节点的位置,val为插入节点的值,如果index等于0,则和头插法过程相同。否则,需要先找到前一个节点, 将该节点的next指针指向前一个节点next指向的节点,然后再将前一个节点指向该节点。
插入过程图解如下:
(1)创建要插入的节点对象
链表及其基本操作(Java语言版)_第1张图片
(2)current节点指向next节点
链表及其基本操作(Java语言版)_第2张图片
(3)previous节点指向current节点,完成插入
链表及其基本操作(Java语言版)_第3张图片

	public void insert(int index, int val) {
		if (index > size) {
			System.out.println("越界");
			return;
		}
		ListNode newListNode = new ListNode(); // 存放添加元素的链表节点
		newListNode.val = val;
		if (index == 0) { // 插入到第0位,和头插法相同
			if (headNode == null) {
				headNode = newListNode;
			} else {
				newListNode.next = headNode;
				headNode = newListNode;
			}
			size ++;		
		} else {
			ListNode prevNode = headNode; // 创建一个指向头结点的指针
			for (int i = 0; i < index-1; i++) {
				prevNode = prevNode.next; // 用该指针找到要添加元素位置的前一个
			}
			newListNode.next = prevNode.next; // 添加的节点的next指针指向要添加位置的下一个
			prevNode.next = newListNode; //前一个节点的next指针指向添加的节点
			size ++;
		}
	}

3.尾插法

先找到原先最后一个节点,然后将其next指向要插入的节点即可。

	public void tailInsertion(int val) {
		ListNode newListNode = new ListNode();
		newListNode.val = val; 
		ListNode prevNode = headNode; // 创建一个指向头结点的指针
		for (int i = 0; i < size-1; i++) {
			prevNode = prevNode.next;// 用该指针找到要最后一个节点的前一个节点
		}
		newListNode.next = prevNode.next;
		prevNode.next = newListNode;
		size ++;
	}

4.删除第index个元素

找到第index-1个节点,将其直接指向第index+1个节点即可。需要注意空链表或者越界情况。
删除过程图解如下:
(1)找到要删除的节点对象current
在这里插入图片描述
(2)previous指针指向next节点即可删除。
在这里插入图片描述

	public void deleteByIndex(int index) {
		if (index > size) {
			System.out.println("越界");
			return;
		}
		if (headNode == null) {
			System.out.println("空链表");
		} else {
			if (index == 0) { // 删除第一个元素,直接把头结点指向第二个元素即可
				headNode = headNode.next;
				size --;
			} else {
				ListNode prevNode = headNode; // 创建一个指向头结点的指针
				for (int i = 0; i < index-1; i++) {
					prevNode = prevNode.next; // 用该指针找到要删除元素位置的前一个
				}
				System.out.println("delete:"+prevNode.next.val);
				// 要删除的元素的前一个节点的next指针指向下一个节点,完成删除
				prevNode.next = prevNode.next.next; 
				size --;
			}
		}
	}

5.删除val元素

删除值为val的节点,以此遍历找到值为val的节点,并对其删除即可。

	public void deleteByValue(int val) {
		if (headNode == null) {
			System.out.println("空链表");
		} else {
			while(val == headNode.val){// 如果头结点是要删除的元素
				headNode = headNode.next;
				size --;
			}
			ListNode prevNode = headNode; // 创建指针,指向被操作的节点的前一位
			ListNode currentNode = headNode; // 创建指针,指向被操作的节点
			while(prevNode != null && currentNode != null){
				if(currentNode.val == val) {
					prevNode.next = currentNode.next;
					size--;
				} else {
					prevNode = currentNode;
				}
				currentNode = currentNode.next;
			}
		}
	}

6.获取第index节点的值

遍历找到即可。

	public void getElum(int index) {
		if (index > size) {
			System.out.println("越界");
			return;
		}
		ListNode currentNode = headNode;
		for (int i = 0; i < index; i++) {
			currentNode = currentNode.next;
		}
		int result = currentNode.val;
		System.out.println(result);
	}

7.将第Index个节点的值修改为val

找到第index节点,修改val即可。

	public void update(int index, int val) {
		if (index > size) {
			System.out.println("越界");
			return;
		}
		ListNode currentNode = headNode;
		for (int i = 0; i < index; i++) {
			currentNode = currentNode.next;
		}
		currentNode.val = val;
	}

8.创建一个链表

	// 创建一个链表  1->2
	public void creatLinkList() {
		ListNode node1 = new ListNode();
		node1.val = 2;
		ListNode node2 = new ListNode();
		node2.val = 1;
		node1.next = node2;
		headNode = node1;
		size++;
		size++;
	}

9.按顺序打印链表

	public void display(){
		ListNode current = headNode;
		while(current != null){
			System.out.print(current.val);
			if (current.next != null) {
				System.out.print("->");
			}
			current = current.next;
		}
		System.out.println();
	}

三、最终代码展示(包含测试)

package linkedList;

public class SingleLinkedList {

	//定义节点
	static class ListNode {
		int val; // 链表存储的值
		ListNode next; // 指针,指向下一个节点
		ListNode() {
			
		}
		ListNode(int val) { 
			this.val = val; 
		}
		ListNode(int val, ListNode next) { 
			this.val = val;
			this.next = next; 
		}
	}
	
	int size; // 链表长度
	ListNode headNode; //链表头结点
//	ListNode dummyHead = new ListNode(0);
	public SingleLinkedList() {
		headNode = null;
		size = 0;
	}
	
	//头插法
	public void headInsertion(int val) {
		ListNode newListNode = new ListNode(); // 新建一个对象,即为要插入的节点
		newListNode.val = val; // 对节点存入要插入的值
		if (headNode == null) { //头结点为空即是个空链表,头结点直接指向要插入的节点即可
			headNode = newListNode;
		} else {
			newListNode.next = headNode; // 要插入的节点应放在第一个,所以它的下一个节点指向head
			headNode = newListNode; // 插入后新插入的节点是新的头结点
		}
		size ++;
	}
	
	//头插法(使用虚拟头结点的方法)
//	public void headInsertionVirtual(int val) {
//		ListNode dummyHead = new ListNode(0, this.head);
//		ListNode newListNode = new ListNode();
//		newListNode.val = val;
//		ListNode currentNode = dummyHead;
//		currentNode.val = val;
//		currentNode.next = dummyHead.next;
//		size ++;
//	}
	
	//任意位置插入
	public void insert(int index, int val) {
		if (index > size) {
			System.out.println("越界");
			return;
		}
		ListNode newListNode = new ListNode(); // 存放添加元素的链表节点
		newListNode.val = val;
		if (index == 0) { // 插入到第0位,和头插法相同
			if (headNode == null) {
				headNode = newListNode;
			} else {
				newListNode.next = headNode;
				headNode = newListNode;
			}
			size ++;		
		} else {
			ListNode prevNode = headNode; // 创建一个指向头结点的指针
			for (int i = 0; i < index-1; i++) {
				prevNode = prevNode.next; // 用该指针找到要添加元素位置的前一个
			}
			newListNode.next = prevNode.next; // 添加的节点的next指针指向要添加位置的下一个
			prevNode.next = newListNode; //前一个节点的next指针指向添加的节点
			size ++;
		}
	}
	
	//尾插法
	public void tailInsertion(int val) {
		ListNode newListNode = new ListNode();
		newListNode.val = val; 
		ListNode prevNode = headNode; // 创建一个指向头结点的指针
		for (int i = 0; i < size-1; i++) {
			prevNode = prevNode.next;// 用该指针找到要最后一个节点的前一个节点
		}
		newListNode.next = prevNode.next;
		prevNode.next = newListNode;
		size ++;
	}
	
	//删除第index个元素
	public void deleteByIndex(int index) {
		if (index > size) {
			System.out.println("越界");
			return;
		}
		if (headNode == null) {
			System.out.println("空链表");
		} else {
			if (index == 0) { // 删除第一个元素,直接把头结点指向第二个元素即可
				headNode = headNode.next;
				size --;
			} else {
				ListNode prevNode = headNode; // 创建一个指向头结点的指针
				for (int i = 0; i < index-1; i++) {
					prevNode = prevNode.next; // 用该指针找到要删除元素位置的前一个
				}
				System.out.println("delete:"+prevNode.next.val);
				// 要删除的元素的前一个节点的next指针指向下一个节点,完成删除
				prevNode.next = prevNode.next.next; 
				size --;
			}
		}
	}
	
	//删除val元素
	public void deleteByValue(int val) {
		if (headNode == null) {
			System.out.println("空链表");
		} else {
			while(val == headNode.val){// 如果头结点是要删除的元素
				headNode = headNode.next;
				size --;
			}
			ListNode prevNode = headNode; // 创建指针,指向被操作的节点的前一位
			ListNode currentNode = headNode; // 创建指针,指向被操作的节点
			while(prevNode != null && currentNode != null){
				if(currentNode.val == val) {
					prevNode.next = currentNode.next;
					size--;
				} else {
					prevNode = currentNode;
				}
				currentNode = currentNode.next;
			}
		}
	}
	
	public void getElum(int index) {
		if (index > size) {
			System.out.println("越界");
			return;
		}
		ListNode currentNode = headNode;
		for (int i = 0; i < index; i++) {
			currentNode = currentNode.next;
		}
		int result = currentNode.val;
		System.out.println(result);
	}
	
	public void update(int index, int val) {
		if (index > size) {
			System.out.println("越界");
			return;
		}
		ListNode currentNode = headNode;
		for (int i = 0; i < index; i++) {
			currentNode = currentNode.next;
		}
		currentNode.val = val;
	}
	
	// 创建一个链表  1->2
	public void creatLinkList() {
		ListNode node1 = new ListNode();
		node1.val = 2;
		ListNode node2 = new ListNode();
		node2.val = 1;
		node1.next = node2;
		headNode = node1;
		size++;
		size++;
	}
	
	
	//打印链表
	public void display(){
		ListNode current = headNode;
		while(current != null){
			System.out.print(current.val);
			if (current.next != null) {
				System.out.print("->");
			}
			current = current.next;
		}
		System.out.println();
	}
	
	public static void main(String[] args) {
		SingleLinkedList singleLinkedList = new SingleLinkedList();

		singleLinkedList.creatLinkList();
		singleLinkedList.headInsertion(5);
		singleLinkedList.headInsertion(8);
		singleLinkedList.headInsertion(9);
		singleLinkedList.headInsertion(5);
		singleLinkedList.display();
		
		singleLinkedList.insert(0,3);
		singleLinkedList.insert(0,3);
		singleLinkedList.insert(2,3);
		singleLinkedList.insert(2,3);
		singleLinkedList.insert(2,7);
		singleLinkedList.display();
		
		singleLinkedList.tailInsertion(1);
		singleLinkedList.display();
		
		singleLinkedList.deleteByIndex(1);
		singleLinkedList.display();
		
		singleLinkedList.deleteByValue(3);
		singleLinkedList.display();
		singleLinkedList.getElum(0);

		singleLinkedList.update(1, 6);
		singleLinkedList.display();
	}
}

你可能感兴趣的:(数据结构,链表,java,数据结构)