数据结构实践——链表

最近想刷leetcode,看了一些大神的经验,说可以从数据结构方面入手,选择一种数据数据然后开始刷这一数据结构的LeetCode中的题目,我的第一种数据结构为链表。以下是链表的常用方法和链表的应用,希望对大家有帮助。

链表的定义比较简单,因此就不进行赘述了,大家可以看看C语言版的数据结构或者王道的数据结构,介绍的都很清楚,下面主要看代码,其中一些应用方法也标注了简单的思路。

package structure_linkedList;

public class Node {
	int value;
	Node next = null;

	public Node(int value) {
		this.value = value;
	}

}
package structure_linkedList;

/*
 * 参考文档链接:
 * https://www.cnblogs.com/llfy/p/9395936.html
 * https://www.cnblogs.com/keeya/p/9218352.html
 * https://blog.youkuaiyun.com/u010292561/article/details/80444057
*/
public class Link {

	Node head = null;
//  链表基本方法(插入、删除、长度、打印)
//	插入值为val的节点(尾插法)
	public void insertNode1(int val) {
		Node newNode = new Node(val);
		if (head == null) {
			head = newNode;
			return;
		}
		Node temp = head;
		while (temp.next != null) {
			temp = temp.next;
		}
		temp.next = newNode;
	}
	
// 插入值为val的节点(头插法)
	public void insertNode2(int val){
		Node node = new Node(val);
		if(head == null) {
			head = node;
			return;
		}
		node.next = head.next;
		head.next = node;
	}

//	删除第index个节点
	public boolean deleteNode(int index) {
		if (index < 1 || index > length())
			return false;
		if (index == 1) {
			head = head.next;
			return true;
		}
		Node preNode = head;
		int i = 1;
		while (i < index - 1) {
			preNode = preNode.next;
		}
		preNode.next = preNode.next.next;
		return true;
	}

//	打印链表
	public void printList() {
		Node temp = head;
		while (temp != null) {
			System.out.print(temp.value + " ");
			temp = temp.next;
		}
		System.out.println();
	}

//	链表的长度
	public int length() {
		int len = 0;
		Node temp = head;
		while (temp != null) {
			temp = temp.next;
			len++;
		}
		return len;
	}
	
//	常用方法:
//	1. 链表反转(遍历法)
//	思路:如果有三个结点,node为当前结点,pre为前一个结点,next为后一个
	public Node reverse1(Node node) {
		Node pre = null;
		Node next = null;
		while(node != null) {
			// next为node的后一个结点
			next = node.next;
			// 反转,当前结点node的next为pre
			node.next = pre;
			// 让pre指向当前结点node(pre指向后移)
			pre = node;
			// 让node指向结点next(node指向后移)
			node = next;
		}
		return pre;
		
	}
	
//	2. 链表反转(递归法)
	public Node reverse2(Node node) {
		if(node == null || node.next == null) {
			return node;
		}
		Node next = node.next;
		Node newHead = reverse2(next);
		next.next = node;
		node.next = null;
		return newHead;
	}

//	3.逆序输出链表(递归法)
	public void reversePrint(Node node) {
		if (node != null) {
			Node next = node.next;
			reversePrint(next);
			System.out.print(node.value+" ");
		}
	}
	
//	4.删除重复节点
	public void deleteDuplecate(Node node) {
		while (node != null) {
			Node pre = node;
			Node next = node.next;
			while (next != null) {
				if (node.value == next.value) {
					pre.next = next.next;
				}
				next = next.next;
				pre = pre.next;
			}
			node = node.next;
		}
	}
	
//	5.链表排序
//	思路:找到一个最小的与第一个节点交换(参考直接选择排序)
	public void sortList(Node node) {
		int temp;
		while (node != null) {
			Node next = node.next;
			while (next != null) {
				if (next.value < node.value) {
					temp = next.value;
					next.value = node.value;
					node.value = temp;
				}
				next = next.next;
			}
			node = node.next;
		}

	}
	
//	6.查找单链表的中间节点(快慢指针,快指针每次走两步,慢指针每次走一步)
	public Node searchMid(Node node) {
		Node quick,slow;
		quick = slow = node;
		while(quick != null && quick.next != null) {
			quick = quick.next.next;
			slow = slow.next;
		}
		return slow;
	}
	
//	7.查找倒数第k个元素
//	思路:从头节点开始,找到第k个节点,然后头节点和k节点同时移动,如果k节点到达尾节点,则头节点指向的尾倒数第k个节点
	public Node findElem(Node head, int k) {
		Node kNode;
		int i = 1;
		kNode = head;
		while(i < k) {
			kNode = kNode.next;
			i++;
		}
		while(kNode.next != null) {
			kNode = kNode.next;
			head = head.next;
		}
		return head;
	}
	
//	8.判断是否有环
//	思路:如果快慢指针相遇,则有环
	public boolean isLoop(Node node) {
		Node fast, slow;
		fast = slow = node;
		if(fast == null) {
			return false;
		}
		while (fast != null || fast.next != null) {
			fast = fast.next.next;
			slow = slow.next;
			if (fast == slow) {
				return true;
			} 
		}

		return false;
	}
	
//	9.求环的长度
//	思路:快慢指针相遇,慢指针再走一圈到达当前位置,走过的路径为长度
	private int loopLength(Node node) {
		Node fast, slow, meet = null;
		fast = slow = node;
		while (fast != null || fast.next != null) {
			fast = fast.next.next;
			slow = slow.next;
			if (fast == slow) {
				meet = slow;
				break;
			} 
		}
		int count = 1;
		slow = slow.next;
		while(slow != meet) {
			slow = slow.next;
			count++;
		}
		return count;
	}
	
//	10.求环的入口
//	思路:依据公式a = (n - 1) * r + (r - b),其中a为头节点到入口的距离,r为环的长度,b为入口到相遇节点之间的距离,n为快指针在环内走的圈数;
//	推导过程:
//	(1)依据快指针走的路程为慢指针走的路径的2倍可以得到 公式一: 2(a + b) = n * r + a + b  
//	(2)化简后得到公式二:a = (n - 1) * r + (r - b),此公式的含义为一个节点A从头节点出发,节点B从相遇点出发,当节点A和节点B相遇时,此节点为环的入口
	private Node loopPort(Node node) {
		Node fast, slow;
		Node nodeA, nodeB = null;
		fast = slow = nodeA = node;
		while (fast != null || fast.next != null) {
			fast = fast.next.next;
			slow = slow.next;
			if (fast == slow) {
				nodeB = slow;
				break;
			} 
		}
		while(nodeA != nodeB) {
			nodeA = nodeA.next;
			nodeB = nodeB.next;
		}
		return nodeA;
	}
	
	public static void main(String[] args) {
		Link list = new Link();
		System.out.println("the length of linkList:" + list.length());
		// 尾插入5和3    5,3
		list.insertNode1(5);
		list.insertNode1(3);
		list.printList();
		// 反转列表    3,5
		list.head = list.reverse2(list.head);
		list.printList();
		// 删除第一个结点3    5
		list.deleteNode(1);
		list.printList();
		// 头插入7    7,5
		list.insertNode2(7);
		list.printList();
		list.insertNode1(1);
		list.insertNode1(2);
		list.insertNode1(3);
		list.insertNode1(4);
		// 查找倒数第3个元素
		System.out.println(list.findElem(list.head, 3).value);
		list.printList();
		//	链表排序
		list.sortList(list.head);
		//	逆序输出链表(递归法)
		list.reversePrint(list.head);
		list.printList();
		//	删除重复数字
		list.deleteDuplecate(list.head);
		list.printList();
		System.out.println("the length of linkList:" + list.length());
		list.printList();
		
//		测试是否有环、环的长度和环的入口  1->2->3->4->5->6->3 环的长度为4,入口为3
		/*Node node1 = new Node(1);
		Node node2 = new Node(2);
		Node node3 = new Node(3);
		Node node4 = new Node(4);
		Node node5 = new Node(5);
		Node node6 = new Node(6);
		list.head = node1;
		node1.next = node2;
		node2.next = node3;
		node3.next = node4;
		node4.next = node5;
		node5.next = node6;
		node6.next = node3;
		System.out.println(list.isLoop(list.head));
		System.out.println(list.loopLength(list.head));
		System.out.println(list.loopPort(list.head).value);*/
		
	}

}

下一篇写二叉树

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值