算法实现----链表(二)

本文深入探讨单向链表的高级操作技巧,包括部分反转、回文判断及按值分区,提供两种不同复杂度的解决方案,适用于各种链表处理场景。

反转部分单向链表

public class ReversePartLinked {
	
	/*要求:给定一个单向链表的头节点head,以及两个整数from和to,
	 *                   在单向链表上把第from个节点到第to个节点这一部分进行反转。
	 *                   时间复杂度为O(n),空间复杂度为O(1)
	 * 例如:
	 *    1>2>3>4>5>null,from=2,to=4
	 *              反转后:1>4>3>2>5>null
	 *再如:
	 *	  1>2>3>null,from=1,to=3
	 *	      反转后:3>2>1>null
	 */
	public static void main(String[] args) {
		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);
		node1.next = node2;
		node2.next = node3;
		node3.next = node4;
		node4.next = node5;
		node5.next = null;
		Node newNode = reversePartLinked(node1, 2, 5);
		while(newNode != null) {
			System.out.println(newNode.value);
			newNode = newNode.next;
		}
	}
	
	public static Node reversePartLinked(Node head,int from,int to) {
		int count = 0;
		Node cur = head;
		Node fpre = null;
		Node tpos = null;
		while(cur != null) {
			count++;
			fpre = count == from - 1?cur:fpre;
			tpos = count == to + 1?cur:tpos;
			cur = cur.next;
		}
		if(head == null || from >= to || from < 1 || to > count) {
			return head;
		}
		Node pre = fpre == null?head:fpre.next;
		Node cur2 = pre.next;
		pre.next = tpos;
		Node next = null;
		while(cur2 != tpos) {
			next = cur2.next;
			cur2.next = pre;
			pre = cur2;
			cur2 = next;
		}
		if(fpre == null) {
			return pre;
		}
		fpre.next = pre;
		return head;
		
	}
}

判断一个单向链表是否是回文结构

public class PalindromeLinked {
	/**
	 **要求:给定一个链表的头节点head,请判断该链表是否为回文结构
	 **例如:
	 *		1>2>1   	返回true
	 *		1>2>2>1		返回true
	 *		1>2>3		返回false
	 **进阶:如果链表长度为n,时间复杂度达到O(n),额外时间复杂度达到O(1)
	 */
	public static void main(String[] args) {
		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(2);
		Node node7 = new Node(1);
		node1.next = node2;
		node2.next = node3;
		node3.next = node4;
		node4.next = node5;
		node5.next = node6;
		node6.next = node7;
		node7.next = null;
		boolean palindrome01 = isPalindrome01(node1);
		boolean palindrome02 = isPalindrome02(node1);
		System.out.println(palindrome01 +" "+palindrome02);
		while(node1 != null) {
			System.out.println(node1.value);
			node1 = node1.next;
		}
	}
	
	//时间复杂度为O(n),空间复杂度为O(n),利用栈结构将链表的一半元素压入栈中
	public static boolean isPalindrome01(Node head) {
		if(head == null || head.next == null) {
			return true;
		}
		if(head.next.next == null) {
			return head.next.value == head.value;
		}
		Stack<Integer> stack = new Stack<>();
		Node slow = head;
		Node quick = head;
		while(quick.next != null && quick.next.next != null) {
			stack.add(slow.value);
			slow = slow.next;
			quick = quick.next.next;
		}
		if(quick.next != null) {
			stack.add(slow.value);
		}
		slow = slow.next;
		while(!stack.isEmpty()) {
			if(stack.pop() != slow.value){
				return false;
			}
			slow = slow.next;
		}
		return true;
	}
	//时间复杂度O(n),空间复杂度O(1)
	/*思路:将链表的右半部分反转,使得左右两部分最后都指向中间节点
	 **    再从两边遍历依次比较,即可得出结果,最后再将链表右半部分恢复原状
	 * */
	public static boolean isPalindrome02(Node head) {
		if(head == null || head.next == null) {
			return true;
		}
		Node node1 = head;
		Node node2 = head;
		//寻找中间节点
		while(node2.next != null && node2.next.next != null) {
			node1 = node1.next;
			node2 = node2.next.next;
		}
		//反转右半部分
		Node node3 = null;
		node2 = node1.next;
		node1.next = null;
		while(node2 != null) {
			node3 = node2.next;
			node2.next = node1;
			node1 = node2;
			node2 = node3;
		}
		//判断是否是回文
		boolean flag = true;
		node3 = node1;
		node2 = node1;
		node1 = head;
		while(node1 != null && node2 != null) {
			if(node1.value != node2.value) {
				flag = false;
				break;
			}
			node1 = node1.next;
			node2 = node2.next;
		}
		//恢复右半部分
		node1 = null;
		node2 = node3;
		while(node2 != null) {
			node3 = node2.next;
			node2.next = node1;
			node1 = node2;
			node2 = node3;
		}
		return flag;
	}
}

将单向链表按某值划分为左边小、中间相等、右边大的形式

public class LinkedPartition {
	
	/*
	 **题目:将单向链表按某值划分为左边小、中间相等、右边大的形式
	 *
	 **要求:给定一个链表的头节点head,节点的值都是整型,再给定一个整数pivot。
	 *		实现一个调整链表的函数,将链表调整为左部分都是值小于pivot的节点,
	 *	           中间部分都是等于pivot的节点,右半部分都是大于pivot的节点。
	 *
	 **例如:
	 *		9>0>4>5>1,pivot=3        调整后是0>1>9>5>4还是1>0>4>9>5都满足条件
	 *
	 **进阶问题:
	 * 		在左、中、右三部分内部也做顺序要求,要求里面的节点从左到右的顺序与原链表中节点的先后次序一致,
	 * 		并且时间复杂度达到O(n),空间复杂度达到O(1)
	 * */
	public static void main(String[] args) {
		Node node1 = new Node(5);
		Node node2 = new Node(0);
		Node node3 = new Node(4);
		Node node4 = new Node(6);
		Node node5 = new Node(5);
		node1.next = node2;
		node2.next = node3;
		node3.next = node4;
		node4.next = node5;
		node5.next = null;
		//Node newNode = linkedPartitionSolution01(node1, 5);
			
		Node newNode = linkedPartitionSolution02(node1, 5);
		while(newNode != null) {
			System.out.println(newNode.value);
			newNode = newNode.next;
		}
	}
	
	//解法一:时间复杂度O(n),空间复杂O(n),利用临时数组
	//
	public static Node linkedPartitionSolution01(Node head,int pivot) {
		if(head == null || head.next == null) {
			return head;
		}
		
		Node cur = head;
		int count = 0;
		while(cur != null) {
			count++;
			cur = cur.next;
		}
		
		cur = head;
		Node[] nodeArr = new Node[count];
		for(int i = 0;i < nodeArr.length ;i++) {
			nodeArr[i] = cur;
			cur = cur.next;
		}
		//类似荷兰国旗问题,分块
		int smaller = -1;
		int bigger = nodeArr.length;
		for(int i = 0;i < bigger;i++) {
			if(nodeArr[i].value < pivot) {
				smaller++;
				Node temp = nodeArr[smaller];
				nodeArr[smaller] = nodeArr[i];
				nodeArr[i] = temp;
			}else if(nodeArr[i].value > pivot) {
				bigger--;
				Node temp = nodeArr[bigger];
				nodeArr[bigger] = nodeArr[i];
				nodeArr[i] = temp;
				i--;
			}
		}
		//拼接链表节点
		for(int i = 0;i < nodeArr.length-1;i++) {
			nodeArr[i].next = nodeArr[i+1];
		}
		nodeArr[nodeArr.length - 1].next = null;
		return nodeArr[0];
	}
	
	//进阶解法:时间复杂度O(n),空间复杂O(1)
	/*
	 * 思路:分别为三个区域创建头节点和尾节点,遍历原链表,将链表每个节点挂至对应的区域
	 * 		最后将三个区域连接起来。
	 * */
	public static Node linkedPartitionSolution02(Node head,int pivot) {
		
		if(head == null || head.next == null) {
			return head;
		}
		
		Node cur = head;
		Node smaller = null;
		Node hSmaller = null;
		Node equal = null;
		Node hEqual = null;
		Node bigger = null;
		Node hBigger = null;
		Node next = null;
		//遍历分别拼接3部分
		while(cur != null) {
			next = cur.next;
			cur.next = null;
			if(cur.value > pivot) {
				if(hBigger == null) {
					bigger = cur;
					hBigger = cur;
				}else {
					bigger.next = cur;
					bigger = cur;
				}
			}else if(cur.value == pivot) {
				if(hEqual == null) {
					equal = cur;
					hEqual = cur;
				}else {
					equal.next = cur;
					equal = cur;
				}
			}else {
				if(hSmaller == null) {
					smaller = cur;
					hSmaller = cur;
				}else {
					smaller.next = cur;
					smaller = cur;
				}
			}
			cur = next;
		}

		if(hSmaller != null) {
			if(hEqual != null) {
				smaller.next = hEqual;
				equal.next = hBigger;
			}else {
				smaller.next = hBigger;
			}
			return hSmaller;
		}
		
		if(hEqual != null) {
			equal.next = hBigger;
			return hEqual;
		}
		
		return hBigger;
		
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值