java实现单链表的各种操作

最近整理了一下关于单链表的各种算法题,代码如下,欢迎大家提问与转载,转载请注明出处

 

package com.special;

import java.util.HashSet;
import java.util.Set;
import java.util.Stack;

public class LinkedList {
	//新建一个单链表
	public ListNode getLinkedList(int n) {
		ListNode head = new ListNode(1);
		ListNode cur = head;
		for(int i = 2;i <= n;i ++) {
			ListNode temp = new ListNode(i);
			cur.next = temp;
			cur = temp;
		}		
		return head;
	}
	
	//获取单链表的长度
	public int getLinkedListLength(ListNode head) {
		if(head == null)	return 0;		
		int count = 0;
		ListNode temp = head;
		while(temp != null) {
			count ++;
			temp = temp.next;
		}		
		return count;
	}
	
	/*删除链表中某个节点,当该节点不是最后一个节点时,时间复杂度是O(1)
	 *当该节点是最后一个节点时,需要循环遍历找到该节点的前一个节点,时间复杂度是O(n)
	 *思路:把当前节点后面一个节点的值赋给当前节点,然后把后面一个节点删除,相当于删除了当前节点
	 */
	public void deleteNode(ListNode head, ListNode node) {
		if(head == null || node == null)	return;		
		if(node.next != null) {		//不是最后一个节点
			ListNode next = node.next;
			node.val = next.val;
			node.next = next.next;
		}
		else {
			ListNode pre = head;	//是最后一个节点
			ListNode cur = head.next;
			while(cur != null) {
				if(cur == node) {
					pre.next = null; //令尾节点的前一个节点的next为空,可以删除尾节点
					break;
				}
				else {
					pre = pre.next;
					cur = cur.next;
				}
			}
		}
	}
	
	//打印单链表
	public void printLinkedList(ListNode node) {
		while(node != null) {
			System.out.print(node.val + " ");
			node = node.next;
		}
	}
	
	/*查找单链表中倒数第k个节点
	 *快指针先走k步,然后慢指针才开始走
	 */
	public ListNode findKNode(ListNode node, int k) {
		if(node == null || k <= 0)	return null;		
		ListNode fp = node;
		int count = 0;
		while(fp != null) {
			fp = fp.next;
			count ++;
			if(count == k) {
				break;
			}
		}
		
		if(count < k) {	//长度不够
			return null;
		}
		
		ListNode fp2 = node;
		while(fp != null) {
			fp = fp.next;
			fp2 = fp2.next;
		}		
		return fp2;
	}
	
	/*
	 * 查找单链表的中间节点:思路与前面大同小异,定义两个指针均指向头结点,
	 * 一个指针一次走两步,当走到头的时候共走了length/2步,
	 * 另一个指针一次走一步,则当第一个指针走到头的时候
	 * 比第二个指针多走了length/2步,第二个指针刚好走到中间
	 */
	public ListNode findMiddleNode(ListNode head) {
		if(head == null || head.next == null)	return head;
		
		ListNode target = head;
		ListNode temp = head;
		while(temp.next != null && temp.next.next != null) {
			target = target.next;
			temp = temp.next.next;
		}		
		return target;
	}
	
	//反转单链表
	public ListNode reverseList(ListNode head) {
		if(head == null || head.next == null)	return head;
		
		ListNode pre = head;
		ListNode cur = head.next;
		ListNode next = null;
		while(cur != null) {
			next = cur.next;
			cur.next = pre;
			pre = cur;
			cur = next;
		}		
		head.next = null;
		return pre;
	}
	
	//从尾到头打印单链表:法1,先反转,再顺序打印;法2,借助栈
	public void printLinkedListFromTailToHead(ListNode head) {
		if(head == null)	return;
		
		ListNode node = head;
		Stack<ListNode> s = new Stack<ListNode>();
		while(node != null) {
			s.push(node);
			node = node.next;
		}
		
		while(!s.isEmpty()) {
			ListNode temp = s.pop();
			System.out.print(temp.val + " ");
		}
	}
	
	//合并两个有序的单链表:递归法
	public ListNode mergeList(ListNode node, ListNode node2) {
		if(node == null && node2 == null) {
			return null;
		}
		
		if(node == null && node2 != null) {
			return node2;
		}
		
		if(node != null && node2 == null) {
			return node;
		}
		
		ListNode head = null;
		if(node.val > node2.val) {	//node2是头节点
			//head = node2;
			node2.next = mergeList(node, node2.next);
			return node2;
		}
		else {	//node是头节点
			//head = node;
			node.next = mergeList(node.next, node2);
			return node;
		}
		
		//return head;
	}
	
	//合并两个有序的单链表:非递归法
	public ListNode mergeList2(ListNode node, ListNode node2) {
		if(node == null && node2 == null) {
			return null;
		}
		
		if(node == null && node2 != null) {
			return node2;
		}
		
		if(node != null && node2 == null) {
			return node;
		}
		
		//拿到头部
		ListNode mergeHead = null;
		if(node.val > node2.val) {	
			mergeHead = node2;
			node2 = node2.next;
		}
		else {
			mergeHead = node;
			node = node.next;
		}
		
		ListNode temp = mergeHead;
		while(node != null && node2 != null) {
			if(node.val > node2.val) {	
				temp.next = node2;
				node2 = node2.next;
			}
			else {
				temp.next = node;
				node = node.next;
			}
			temp = temp.next;
		}
		
		if(node != null) {	//链表本来就是直接接上去
			temp.next = node;
		}
		
		if(node2 != null) {
			temp.next = node2;
		}		
		return mergeHead;
	}
	
	//单链表的归并排序
	public ListNode mergeSort(ListNode node) {
		if(node == null || node.next == null)	return node;
		ListNode middleNode = findMiddleNode(node);	//获取中间节点
		ListNode backStart = middleNode.next;
		middleNode.next = null;	//拆分链表
		return mergeList(mergeSort(node), mergeSort(backStart));	
	}
	
	/*
	 * 单链表的插入排序:与数组的插入排序不同,单链表的插入排序只能从前往后遍历(画图),
	 * 所以要借助辅助节点fake(因为有可能是第一个节点比待插入节点值大),这题非常能考察对链表和指针的理解
	 */
	public ListNode insertSort(ListNode head) {
		if(head == null || head.next == null)	return head;
		
		ListNode fakeNode = new ListNode(-1);
		fakeNode.next = head;
		ListNode pre = head;	//前后两个指针用于比较前后两个数的大小
		ListNode cur = head.next;
		while(pre.next != null) {
			if(pre.val < cur.val) {	//不用插入,指针右移
				pre = pre.next;
				cur = cur.next;
			}
			else {
				ListNode insertNode = cur;	//将插入节点剥离出来
				cur = cur.next;	
				pre.next = cur;
				
				//从前往后找,找到第一个比cur2大的节点,插到它的后面
				ListNode temp = fakeNode;
				while(temp.next.val < insertNode.val) {
					temp = temp.next;
				}
				ListNode tempNext = temp.next;
				temp.next = insertNode;
				insertNode.next = tempNext;
			}
		}		
		return head;
	}
	
	//判断一个单链表中是否有环:法一,定义快慢指针,若快慢指针能相遇,则说明存在环(类似于环形赛道)。
	public boolean hasLoop(ListNode head) {
		if(head == null || head.next == null)	return false;	//若链表仅存在头节点,不认为是环	
		
		boolean flag = false;
		ListNode pFast = head;
		ListNode pSlow = head;
		while(pFast.next != null && pFast.next.next != null) { //这个地方不能判断pFast.next.next != null,否则会跳出空指针错误	
			pFast = pFast.next.next;
			pSlow = pSlow.next;
			if(pFast == pSlow) {
				flag = true;
				break;
			}
		}		
		return flag;
	}
	
	//判断一个单链表中是否有环:法二,当判断某个节点时,定义一个辅助容器暂存前面所有节点,若在辅助容器里找到与该节点相同的节点,则认为存在环。
	public boolean hasLoopUsingSet(ListNode head) {
		if(head == null || head.next == null)	return false;	//若链表仅存在头节点,不认为是环
		
		Set<ListNode> set = new HashSet<ListNode>();
		ListNode temp = head;
		while(temp != null) {
			if(set.contains(temp)) {
				return true;
			}	
			else {
				set.add(temp);
				temp = temp.next;
			}
		}		
		return false;
	}
	
	//已知一个链表里有环,求进入该环的第一个节点
	public ListNode getFirstNodeInCycleUsingSet(ListNode head) {
		if(head == null || head.next == null)	return null;
		
		Set<ListNode> set = new HashSet<ListNode>();
		ListNode temp = head;
		while(temp != null) {
			if(set.contains(temp)) {	//Set contains()方法的查询速度比List快很多,不要用List
				return temp;
			}
			set.add(temp);
			temp = temp.next;
		}	
		return null;
	}
	
	//已知一个链表里有环,求进入该环的第一个节点
	public ListNode getFirstNodeInCycle(ListNode head) {
		if(head == null || head.next == null)	return null;
		ListNode pFast = head;
		ListNode pSlow = head;
		while(pFast.next != null && pFast.next.next != null) { //这个地方不能判断pFast.next.next != null,否则会跳出空指针错误	
			pFast = pFast.next.next;
			pSlow = pSlow.next;
			if(pFast == pSlow) {
				break;
			}
		}		
		//此时pSlow只是环里的一个节点,并不是入口节点,需要从头开始跑求环的入口节点
		ListNode temp = head;
		while(temp != null) {
			if(temp == pSlow) {
				return temp;
			}
			else {
				temp = temp.next;
				pSlow = pSlow.next;
			}
		}
		return null;
	}
	
	/*判断两个单链表是否相交:
	 *如果链表都无环(链表里没有重复的数据),则先判断链表的尾指针是否一样,如果不一样,则没有相交。(这一步可以不做)
	 *如果一样,则找出两个链表的长度差,长的链表先走差值的步数,然后两个链表再一起走,
	 *如果相交,则必然有一处扫描节点相同。
	 */
	public ListNode getIntersectNode(ListNode node, ListNode node2) {
		if(node == null || node2 == null)
			return null;
		
		ListNode target = null;
		ListNode pTail = findKNode(node, 1);
		ListNode pTail2 = findKNode(node2, 1);		
		if(pTail != pTail2) {
			return target;
		}
		
		int len = getLinkedListLength(node);
		int len2 = getLinkedListLength(node2);
		if(len < len2) {
			int i = 0;
			while(i < len2 - len) {
				node2 = node2.next;
				i ++;
			}
		}		
		else if(len > len2) {
			int j = 0;
			while(j < len - len2) {
				node = node.next;
				j ++;
			}
		}
				
		while(node != null && node2 != null) {
			if(node == node2) {		//两个节点相等,则其后面的节点也相等
				target = node;
				break;
			}
			node = node.next;
			node2 = node2.next;
		}		
		return target;
	}
	
	/*删除链表的奇数节点
	 * 1-2-3-4-5,删除后 2-4
	 * 1-2-3-4-5-6,删除后2-4-6
	 */
	public ListNode deleteOddNode(ListNode head) {
		ListNode fake = new ListNode(-1);
		fake.next = head;
		ListNode pre = fake;
		ListNode cur = null;
		while(pre != null && pre.next != null) {
			cur = pre.next;
			pre.next = cur.next;
			pre = pre.next;
		}
		return fake.next;
	}
	
	public static void main(String[] args) {
		LinkedList t = new LinkedList();
		ListNode l1 = new ListNode(1);
		ListNode l2 = new ListNode(2);
		ListNode l3 = new ListNode(3);
		ListNode l4 = new ListNode(4);
		ListNode l5 = new ListNode(5);
		l1.next = l2;
		l2.next = l3;
		l3.next = l4;
		l4.next = l5;
//		l5.next = l3;
//		System.out.println(t.getFirstNodeInCycle(l1).val);
		
		ListNode n1 = new ListNode(6);
//		ListNode n2 = new ListNode(4);
//		ListNode n3 = new ListNode(5);
		n1.next = l4;
		ListNode res = t.getIntersectNode(l1, n1);
		if(res != null) {
			System.out.println(res.val);
		}
    
	}
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值