java数据结构与算法3---链表、栈与队列(2)

java数据结构与算法3---链表、栈与队列(2)


3种结构算法实战  (续)

题目八 (在行列都排好序的矩阵中找数)

题目八:给定一个有N*M的整型矩阵matrix和一个整数K,matrix的每一行和每一 列都是排好序的。实现一个函数,判断K是否在matrix中。例如: 0   1   2   5-- 2   3   4   7 --4   4   4   8-- 5   7   7   9 如果K为7,返回true;如果K为6,返回false.【要求】 时间复杂度为O(N+M),额外空间复杂度为O(1)。 

    思路:由于行列都排好序的矩阵,先指定一个起点n来找K。如果K>n则往下找,如果K<n则往左找,若果K=n则找到K。实例如下图:

代码实现如下:

//行列排好序的矩阵找数算法
	public static boolean isContains(int[][] matrix, int K) {
		int row = 0;
		int col = matrix[0].length - 1;
		while (row < matrix.length && col > -1) {
			if (matrix[row][col] == K) {
				return true;
			} else if (matrix[row][col] > K) {
				col--;
			} else {
				row++;
			}
		}
		return false;
	}

 题目九 (打印两个有序链表的公共部分)

题目九:给定两个有序链表的头指针head1和head2,打印两个链表的公共部分。 

思路:由利用有序链表的有序性,从链表头来比较链表值的大小,将值小的链表后移在比骄;如果链表值相等则为公共部分,即打印并将两个链表都后移一位再比较直至链表尾结束。

代码实现如下:

//打印两个有序链表的公共部分算法

//构造链表节点
public static class Node {
	public int value;
	public Node next;
	public Node(int data) {
		this.value = data;
	}
}
//寻找链表公共部分并打印
public static void printCommonPart(Node head1, Node head2) {
	System.out.print("Common Part: ");
	while (head1 != null && head2 != null) {
		if (head1.value < head2.value) {
			head1 = head1.next;
		} else if (head1.value > head2.value) {
			head2 = head2.next;
		} else {
			System.out.print(head1.value + " ");
			head1 = head1.next;
			head2 = head2.next;
		}
	}
	System.out.println();
}
//打印整个链表
public static void printLinkedList(Node node) {
	System.out.print("Linked List: ");
	while (node != null) {
		System.out.print(node.value + " ");
		node = node.next;
	}
	System.out.println();
}

题目十 (判断一个链表是否为回文结构)

题目十: 给定一个链表的头节点head,请判断该链表是否为回文结构。 例如: 1->2->1,返回true。 1->2->2->1,返回true。15->6->15,返回true。 1->2->3,返回false。 

进阶: 如果链表长度为N,时间复杂度达到O(N),额外空间复杂度达到O(1)。

思路:会问结构的正序逆序都一样。在不限定额外空间复杂度的时候,利用栈结构来将链表倒序判断是否是回文结构;在限定额外空间复杂度为O(1)时,将链表从中间截断并将后半部分转换链表方向进而判断是否为回文,最后还用还原为原链表结构。实例如下图:

代码实现如下:

//回文链表判断算法
public static class Node {
	public int value;
	public Node next;

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

// need n extra space
public static boolean isPalindrome1(Node head) {
	Stack<Node> stack = new Stack<Node>();
	Node cur = head;
	while (cur != null) {
		stack.push(cur);
		cur = cur.next;
	}
	while (head != null) {
		if (head.value != stack.pop().value) {
			return false;
		}
		head = head.next;
	}
	return true;
}

// need n/2 extra space
public static boolean isPalindrome2(Node head) {
	if (head == null || head.next == null) {
		return true;
	}
	Node right = head.next;
	Node cur = head;
	while (cur.next != null && cur.next.next != null) {
		right = right.next;
		cur = cur.next.next;
	}
	Stack<Node> stack = new Stack<Node>();
	while (right != null) {
		stack.push(right);
		right = right.next;
	}
	while (!stack.isEmpty()) {
		if (head.value != stack.pop().value) {
			return false;
		}
		head = head.next;
	}
	return true;
}

// need O(1) extra space
public static boolean isPalindrome3(Node head) {
	if (head == null || head.next == null) {
		return true;
	}
	Node n1 = head;
	Node n2 = head;
	while (n2.next != null && n2.next.next != null) { // find mid node
		n1 = n1.next; // n1 -> mid
		n2 = n2.next.next; // n2 -> end
	}
	n2 = n1.next; // n2 -> right part first node
	n1.next = null; // mid.next -> null
	Node n3 = null;
	while (n2 != null) { // right part convert
		n3 = n2.next; // n3 -> save next node
		n2.next = n1; // next of right node convert
		n1 = n2; // n1 move
		n2 = n3; // n2 move
	}
	n3 = n1; // n3 -> save last node
	n2 = head;// n2 -> left first node
	boolean res = true;
	while (n1 != null && n2 != null) { // check palindrome
		if (n1.value != n2.value) {
			res = false;
			break;
		}
		n1 = n1.next; // left to mid
		n2 = n2.next; // right to mid
	}
	n1 = n3.next;
	n3.next = null;
	while (n1 != null) { // recover list
		n2 = n1.next;
		n1.next = n3;
		n3 = n1;
		n1 = n2;
	}
	return res;
}

public static void printLinkedList(Node node) {
	System.out.print("Linked List: ");
	while (node != null) {
		System.out.print(node.value + " ");
		node = node.next;
	}
	System.out.println();
}

题目十一 ( “之”字形打印矩阵)

题目十一:给定一个矩阵matrix,按照 “之”字形的方式打印这个矩阵,例如: 1   2   3   4-- 5   6   7   8-- 9  10  11  12  “之”字形打印的结果为:1,2,5,9,6,3,4,7,10,11, 8,12 。【要求】 额外空间复杂度为O(1)。

 思路:由根据第一行和第一列来构成对角来进行打印,如果行先到尾部就顺着最后一列向下;如果列先到尾部就顺着最后一行向右,最终将整个矩阵以之字形式打印。实例如下图:

代码实现如下:

//之字打印矩阵
public static void printMatrixZigZag(int[][] matrix) {
	int tR = 0;
	int tC = 0;
	int dR = 0;
	int dC = 0;
	int endR = matrix.length - 1;
	int endC = matrix[0].length - 1;
	boolean fromUp = false;
	while (tR != endR + 1) {
		printLevel(matrix, tR, tC, dR, dC, fromUp);
		tR = tC == endC ? tR + 1 : tR;
		tC = tC == endC ? tC : tC + 1;
		dC = dR == endR ? dC + 1 : dC;
		dR = dR == endR ? dR : dR + 1;
		fromUp = !fromUp;
	}
	System.out.println();
}

public static void printLevel(int[][] m, int tR, int tC, int dR, int dC,
	    boolean f) {
	if (f) {
		while (tR != dR + 1) {
			System.out.print(m[tR++][tC--] + " ");
		}
	} else {
		while (dR != tR - 1) {
			System.out.print(m[dR--][dC++] + " ");
		}
	}
}

题目十二(将单向链表按某值划分成左边小、中间相等、右边大的形式)

题目十二:给定一个单向链表的头节点head,节点的值类型是整型,再给定一个 整 数pivot。实现一个调整链表的函数,将链表调整为左部分都是值小于 pivot 的节点,中间部分都是值等于pivot的节点,右部分都是值大于 pivot的节点。 除这个要求外,对调整后的节点顺序没有更多的要求。 例如:链表9->0->4->5>1,pivot=3。 调整后链表可以是1->0->4->9->5,也可以是0->1->9->5->4。总 之,满 足左部分都是小于3的节点,中间部分都是等于3的节点(本例中这个部 分为空),右部分都是大于3的节点即可。对某部分内部的节点顺序不做 要求。
进阶: 在原问题的要求之上再增加如下两个要求。 在左、中、右三个部分的内部也做顺序要求,要求每部分里的节点从左 到右的 顺序与原链表中节点的先后次序一致。 例如:链表9->0->4->5->1,pivot=3。 调整后的链表是0->1->9->4->5。 在满足原问题要求的同时,左部分节点从左到 右为0、1。在原链表中也 是先出现0,后出现1;中间部分在本例中为空,不再 讨论;右部分节点 从左到右为9、4、5。在原链表中也是先出现9,然后出现4, 最后出现5。 如果链表长度为N,时间复杂度请达到O(N),额外空间复杂度请达到O(1)。

 思路:由题意涉及到划分3个区域的问题,自然想到快排算法中的partiton方法,但是partition是对于数组来实现的,于是先将链表拆开放入arr中,partiton后在重新链接起来形成链表。进阶:为了避免partiton的不稳定性和空间上的浪费,采用更加直接的方法,直接定义6个Node变量,分别指定小于区域头尾、等于区域头尾、大约区域头尾;然后将链表依次拆开装入指定区域的尾部;最后将这三分区域的头尾相联构成链表。

代码实现如下:

//单链表划分<=>区算法
public static class Node {
	public int value;
	public Node next;

	public Node(int data) {
		this.value = data;
	}
}
//将链表转成arr并partition
public static Node listPartition1(Node head, int pivot) {
	if (head == null) {
		return head;
	}
	Node cur = head;
	int i = 0;
	while (cur != null) {
		i++;
		cur = cur.next;
	}
    Node[] nodeArr = new Node[i];
	i = 0;
	cur = head;
	for (i = 0; i != nodeArr.length; i++) {
		nodeArr[i] = cur;
		cur = cur.next;
	}
	arrPartition(nodeArr, pivot);
	for (i = 1; i != nodeArr.length; i++) {
		nodeArr[i - 1].next = nodeArr[i];
	}
	nodeArr[i - 1].next = null;
	return nodeArr[0];
}
//快排中的partition方法
public static void arrPartition(Node[] nodeArr, int pivot) {
	int small = -1;
	int big = nodeArr.length;
	int index = 0;
	while (index != big) {
		if (nodeArr[index].value < pivot) {
			swap(nodeArr, ++small, index++);
		} else if (nodeArr[index].value == pivot) {
			index++;
		} else {
			swap(nodeArr, --big, index);
		}
	}
}

public static void swap(Node[] nodeArr, int a, int b) {
	Node tmp = nodeArr[a];
	nodeArr[a] = nodeArr[b];
	nodeArr[b] = tmp;
}
//第二种算法:优化稳定性、低空间性、提高了效率
public static Node listPartition2(Node head, int pivot) {
	Node sH = null; // small head
	Node sT = null; // small tail
	Node eH = null; // equal head
	Node eT = null; // equal tail
	Node bH = null; // big head
	Node bT = null; // big tail
	Node next = null; // save next node
	// every node distributed to three lists
	while (head != null) {
		next = head.next;
		head.next = null;
		if (head.value < pivot) {
			if (sH == null) {
				sH = head;
				sT = head;
			} else {
				sT.next = head;
				sT = head;
			}
		} else if (head.value == pivot) {
			if (eH == null) {
				eH = head;
				eT = head;
			} else {
				eT.next = head;
				eT = head;
			}
		} else {
			if (bH == null) {
				bH = head;
				bT = head;
			} else {
				bT.next = head;
				bT = head;
			}
		}
		head = next;
	}
	// small and equal reconnect
	if (sT != null) {
		sT.next = eH;
		eT = eT == null ? sT : eT;
	}
	// all reconnect
	if (eT != null) {
		eT.next = bH;
	}
	return sH != null ? sH : eH != null ? eH : bH;
}

public static void printLinkedList(Node node) {
	System.out.print("Linked List: ");
	while (node != null) {
		System.out.print(node.value + " ");
		node = node.next;
	}
	System.out.println();
}

题目十三 (复制含有随机指针节点的链表 )

题目十三:一种特殊的链表节点类描述如下: 
    public class Node {

          public int value;

          public Node next;

          public  Node rand; 
          public Node(int data)  {  this.value   data;  } 
    } 
    Node类中的value是节点值,next指针和正常单链表中next指针的意义一 样,都指向下一个节点,rand指针是Node类中新增的指针,这个指针可 能指向链表中的任意一个节点,也可能指向null。 给定一个由Node节点类型组成的无环单链表的头节点head,请实现一个 函数完成这个链表中所有结构的复制,并返回复制的新链表的头节点。 进阶:不使用额外的数据结构,只用有限几个变量,且在时间复杂度为 O(N)内完成原问题要实现的函数。

 思路:方法一利用Map<Node,Node>结构,使两个Node相同达到复制的目的,然后利用Node获取原始Node的链next、rand,然后将原始Node的链复制到get(Node)新节点即可;方法二直接在Node后复制相同的CopyNode并让Node-->CopyNode-->Node.next,而Node.rand节点就对应为Node.rand后一个节点,即Node.rand.next,这样就将Node、next、rand都复制完毕。最后将辅助用的next断开将原链表与复制后的链表分开。

代码实现如下:

//特殊链表的复制算法
public static class Node {
	public int value;
	public Node next;
	public Node rand;

	public Node(int data) {
		this.value = data;
	}
}
//利用Map结构实现
public static Node copyListWithRand1(Node head) {
	HashMap<Node, Node> map = new HashMap<Node, Node>();
	Node cur = head;
	while (cur != null) {
		map.put(cur, new Node(cur.value));
		cur = cur.next;
	}
	cur = head;
	while (cur != null) {
		map.get(cur).next = map.get(cur.next);
		map.get(cur).rand = map.get(cur.rand);
		cur = cur.next;
	}
	return map.get(head);
}
//直接实现:优化方法,节省空间
public static Node copyListWithRand2(Node head) {
	if (head == null) {
		return null;
	}
	Node cur = head;
	Node next = null;
	// copy node and link to every node
	while (cur != null) {
		next = cur.next;
		cur.next = new Node(cur.value);
		cur.next.next = next;
		cur = next;
	}
	cur = head;
	Node curCopy = null;
	// set copy node rand
	while (cur != null) {
		next = cur.next.next;
		curCopy = cur.next;
		curCopy.rand = cur.rand != null ? cur.rand.next : null;
		cur = next;
	}
	Node res = head.next;
	cur = head;
	// split
	while (cur != null) {
		next = cur.next.next;
		curCopy = cur.next;
		cur.next = next;
		curCopy.next = next != null ? next.next : null;
		cur = next;
	}
	return res;
}

public static void printRandLinkedList(Node head) {
	Node cur = head;
	System.out.print("order: ");
	while (cur != null) {
		System.out.print(cur.value + " ");
		cur = cur.next;
	}
	System.out.println();
	cur = head;
	System.out.print("rand:  ");
	while (cur != null) {
		System.out.print(cur.rand == null ? "- " : cur.rand.value + " ");
		cur = cur.next;
	}
	System.out.println();
}

题目十四 (两个单链表相交的一系列问题 )

题目十四:在本题中,单链表可能有环,也可能无环。给定两个 单链表的头节点 head1和head2,这两个链表可能相交,也可能不相交。请实现一个函数, 如果两个链表相交,请返回相交的 第一个节点;如果不相交,返回null 即可。 要求:如果链表1的长度为N,链表2的长度为M,时间复杂度请达到 O(N+M),额外空间复杂度请达到O(1)。

思路:如下图分析图所示:

代码实现如下:

public static class Node {
	public int value;
	public Node next;

	public Node(int data) {
		this.value = data;
	}
}
//取得第一个公共节点
public static Node getIntersectNode(Node head1, Node head2) {
	if (head1 == null || head2 == null) {
		return null;
	}
	Node loop1 = getLoopNode(head1);
	Node loop2 = getLoopNode(head2);
	if (loop1 == null && loop2 == null) {
		return noLoop(head1, head2);
	}
    if (loop1 != null && loop2 != null) {
		return bothLoop(head1, loop1, head2, loop2);
	}
	return null;
}
	
/*
*判断是否为有环链表
*确定环节点的解释:通过快慢指针找到链表的中点MidNode
*然后头节点Head与MidNode一起动,当Head==MidNode的时候就找到环节点
*/
public static Node getLoopNode(Node head) {
	if (head == null || head.next == null || head.next.next == null) {
		return null;
	}
	Node n1 = head.next; // n1 -> slow
	Node n2 = head.next.next; // n2 -> fast
	while (n1 != n2) {
		if (n2.next == null || n2.next.next == null) {
			return null;
		}
		n2 = n2.next.next;
		n1 = n1.next;
	}
	n2 = head; // n2 -> walk again from head
	while (n1 != n2) {
		n1 = n1.next;
		n2 = n2.next;
	}
	return n1; //n1为有环链表的环节点
}

/*
*两无环链表的处理
*思路:先确定两链表的长度差,将长链表先移动长度差距离,构成两链表长度相等的状态
*      然后齐头并进至value相等为止找到第一个公共节点
*/
public static Node noLoop(Node head1, Node head2) {
	if (head1 == null || head2 == null) {
		return null;
	}
	Node cur1 = head1;
	Node cur2 = head2;
	int n = 0;
	while (cur1.next != null) {
		n++;
		cur1 = cur1.next;
	}
	while (cur2.next != null) {
		n--;
		cur2 = cur2.next;
	}
	if (cur1 != cur2) {
		return null;
	}
	cur1 = n > 0 ? head1 : head2;
	cur2 = cur1 == head1 ? head2 : head1;
	n = Math.abs(n);
	while (n != 0) {
		n--;
		cur1 = cur1.next;
	}
	while (cur1 != cur2) {
		cur1 = cur1.next;
		cur2 = cur2.next;
	}
	return cur1;
}

/*
*两个有环链表的处理
*①环外相交即loop1==loop2,则loop1以上的部分就使两个无环链表的处理过程
*②环内相交即loop1!=loop2,则loop1或loop2是第一个公共节点
*/
public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
	Node cur1 = null;
	Node cur2 = null;
	if (loop1 == loop2) {
		cur1 = head1;
		cur2 = head2;
		int n = 0;
		while (cur1 != loop1) {
			n++;
			cur1 = cur1.next;
		}
		while (cur2 != loop2) {
			n--;
			cur2 = cur2.next;
		}
		cur1 = n > 0 ? head1 : head2;
		cur2 = cur1 == head1 ? head2 : head1;
		n = Math.abs(n);
		while (n != 0) {
			n--;
			cur1 = cur1.next;
		}
		while (cur1 != cur2) {
			cur1 = cur1.next;
			cur2 = cur2.next;
		}
		return cur1;
	} else {
		cur1 = loop1.next;
		while (cur1 != loop1) {
			if (cur1 == loop2) {
				return loop1;
			}
			cur1 = cur1.next;
		}
		return null;
	}
}

题目十五(二分的小扩展 )

题目十五:二分的小扩展 

思路:待续。。。。

代码实现如下:

//二分的小扩展算法实现
public static int getLessIndex(int[] arr) {
	if (arr == null || arr.length == 0) {
		return -1; // no exist
	}
	if (arr.length == 1 || arr[0] < arr[1]) {
		return 0;
	}
	if (arr[arr.length - 1] < arr[arr.length - 2]) {
		return arr.length - 1;
	}
	int left = 1;
	int right = arr.length - 2;
	int mid = 0;
	while (left < right) {
		mid = (left + right) / 2;
		if (arr[mid] > arr[mid - 1]) {
			right = mid - 1;
		} else if (arr[mid] > arr[mid + 1]) {
			left = mid + 1;
		} else {
			return mid;
		}
	}
	return left;
}

我将在java数据结构与算法4---二叉树中介绍链表、栈与队列3种结构算法实战的余下的题目八——题目十五。

敬请关注! 点赞+关注不迷路哟!

                                                                                                  谢谢阅读               ---by 知飞翀

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值