牛客网 4 — 宏观调度问题

牛客网 4 — 宏观调度问题

宏观调度问题

这种问题不要把自己局限在一个过细节的问题上,而是从宏观上来看。

最优解!!一般从数据状况或者问法出发!!

旋转打印矩阵

1、转圈打印矩阵

在这里插入图片描述

public class printMatrixSpiralOrder {

	public static void spiralOrderPrint(int[][] matrix) {
        //前面四步骤是获得起点终点的位置
		int tR = 0;
		int tC = 0;
		int dR = matrix.length - 1;
		int dC = matrix[0].length - 1;
        //循环操作
		while (tR <= dR && tC <= dC) {
            //移动位置
			print(matrix, tR++, tC++, dR--, dC--);
		}
	}
	
	public static void print(int[][] m, int tR, int tC, int dR, int dC) {
		//首先判断是否位于同一行
		if(tR == dR) {
			//循环打印改行,注意这里循环的是列
			for(int i = tC; i < dC; i++) {
				System.out.println(m[tR][i] +" ");
			}
		}else if(tC == dC) {	//同理,在同一列也是一样
			//循环打印行
			for(int i = tR; i < dR; i++) {
				System.out.println(m[i][tC] +" ");
			}
		}else {			//剩下一种情况就是不在同一行且不在同一列
			
			int curC = tC;
			int curR = tR;
			while(curC != dC) {
				//说明不在同一列,就要打印整一行        tR的那一行,从dR开始
				System.out.println(m[tR][curC++] +" ");
			}
			while(curR != dR) {
				//说明不在同一行,就要打印整一列        dC的那一列  
				System.out.println(m[curR++][dC] +" ");
			}
			while(curC != tC) {
				System.out.println(m[dR][curC--] +" ");
			}
			while(curR != dR) {
				System.out.println(m[curR--][tC] +" ");
			}
		}
	}
}

在这里插入图片描述

每次循环打印都打印最外一层!然后由 spiralOrderPrint 控制函数来控制起点和终点的位置。

在这里插入图片描述

2、旋转正方形

在这里插入图片描述

依次旋转,先转最外面一圈的,然后逐步往里面旋转。每次change只改变四个点的位置,然后循环 i 次。

public class xuanzhuanzhengfangxing {

	public static void rotate(int[][] matrix) {
		//还是一样,先要获得起点与终点
		int tR = 0, tC = 0; //起点
		int dR = matrix.length - 1 , dC = matrix[0].length - 1;	//终点
		
		while(tR < dR) {
			//旋转的方法,这里不存在=的情况,所以有所区别。
			change(matrix, tR++, tC++, dR--, dC--);
		}
	}

	public static void change(int[][] m, int tR, int tC, int dR, int dC) {
		//每一次需要改变的一对位置的数量
		int times = dC - tC; 
		int tmp = 0;
		for (int i = 0; i != times; i++) {
			tmp = m[tR][tC + i];
			m[tR][tC + i] = m[dR - i][tC];
			m[dR - i][tC] = m[dR][dC - i];
			m[dR][dC - i] = m[tR + i][dC];
			m[tR + i][dC] = tmp;
		}	
	}
	public static void printMatrix(int[][] matrix) {
		for (int i = 0; i != matrix.length; i++) {
			for (int j = 0; j != matrix[0].length; j++) {
				System.out.print(matrix[i][j] + "  ");
			}
			System.out.println();
		}
	}

	public static void main(String[] args) {
		int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 },
				{ 13, 14, 15, 16 } };
		printMatrix(matrix);
		rotate(matrix);
		System.out.println("=========");
		printMatrix(matrix);

	}
}

反转链表

1、反转链表

2、反转双向链表

之字打印矩阵

在这里插入图片描述

在这里插入图片描述

public class Code_08_ZigZagPrintMatrix {

	public static void printMatrixZigZag(int[][] matrix) {
        //先初始化 A,B两个点。
		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);
            //然后是进行移动,这里是关键!!!!
            //A:只往右边移动,如果到了最右边,就向下移动
            //B:只向下移动,如果到了最下面,就往右边移动
			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++] + " ");
			}
		}
	}

	public static void main(String[] args) {
		int[][] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };
		printMatrixZigZag(matrix);

	}

}

行列有序的矩阵中找数

在这里插入图片描述

这里要注意好是从小到大还是从大到小的排列。

如果行和列都是从小到大排列的话,就从右上脚开始,并且如果当前数字大于number就要换到前一列。

如果是从大到小排列的话,就要从左上角开始,并且如果当前数字小于number就要换到下一列,因为这个数字下面的元素肯定大于它,就没必要遍历了。

public class FindNumInSortedMatrix {

	public static boolean find(int[][] matrix, int number) {
		int row = 0;
		int col = matrix[0].length - 1;
		while(row < matrix.length && col > -1) {
			if(matrix[row][col] == number) {
				return true;
			}else if(matrix[row][col] > number) {
				col--;
			}else {
				row++;
			}
		}
		return false;
	}
	
	public static void main(String[] args) {
		int[][] matrix = new int[][] { { 0, 1, 2, 3, 4, 5, 6 },// 0
				{ 10, 12, 13, 15, 16, 17, 18 },// 1
				{ 23, 24, 25, 26, 27, 28, 29 },// 2
				{ 44, 45, 46, 47, 48, 49, 50 },// 3
				{ 65, 66, 67, 68, 69, 70, 71 },// 4
				{ 96, 97, 98, 99, 100, 111, 122 },// 5
				{ 166, 176, 186, 187, 190, 195, 200 },// 6
				{ 233, 243, 321, 341, 356, 370, 380 } // 7
		};
		int K = 233;
		System.out.println(find(matrix, K));
	}

}

有序链表的公共部分

在这里插入图片描述

public class lianbiaogonggong {
	
	public static class Node {
		public int value;
		public Node next;
		public Node(int data) {
			this.value = data;
		}
	}
	
	//打印公共部分
	public static void printcommon(Node head1, Node head2) {
		
		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;
			}
		}
	}
}

链表回文结构

在这里插入图片描述

1、可以先使用一个栈来存储,第一次遍历时,边遍历边入栈;第二次遍历时,边遍历边弹栈。

//用栈实现,额外空间为 O(n)
	public static boolean isPalindrome1(Node node) {
		Stack<Node> stack = new Stack<>();
		
		while(node != null) {
			stack.push(node);
			node = node.next;
		}
		while(node != null) {
			if(node.value != stack.pop().value) {
				return false;
			}
			node = node.next;
		}
		return true;
	}

2、快慢指针,找到链表中点位置,然后把后半段逆序(栈),然后再重头遍历。这个不难,就不写了。

3、

在这里插入图片描述

//不用额外空间
	public static boolean isPalindrome2(Node node) {
		Node quick = node; //快指针
		Node slow = node;	//慢指针
		
		//找到中点位置
		while(quick.next.next != null && quick.next != null) {
			quick = quick.next.next;
			slow = slow.next;
		}
		
		//逆序后半段的链表
		quick = slow.next;
		slow.next = null; //中点的next为null
		Node tmp = null; //额外空间
		while(quick != null) {
			tmp = quick.next;
			quick.next = slow;
			slow = quick;
			quick = tmp;
		}
		
		quick = tmp; //quick指向反转链表的开头
		slow = node; //slow指向链表开头
		//遍历链表
		quick = node;
		boolean res = true;
		while(quick != null) {
			if(quick.value != slow.value) {
				res = false;
			}
			quick = quick.next;
			slow = slow.next;
		}
		
		//最后把链表反转回来
		quick = tmp.next;
		tmp.next = null;
		while(quick != null) {
			slow = quick.next;
			quick.next = tmp;
			tmp = quick;
			quick = slow;
		}
		return res;
	}

链表荷兰国旗

在这里插入图片描述

建立三个节点类型的对象,分别时 less 区域,equal 区域,more 区域,然后每次遍历都选择三个符合区域的节点,添加到该区域上,最后把三个区域串起来。

public class SmallerEqualBigger {

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

		public Node(int data) {
			this.value = data;
		}
	}
	
	public static Node listPartition1(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(head.value > pivot) {
				//如果值属于小于区域
				if(bH == null) {
					//如果小于区域还没有节点
					bH = head;
					bT = head;
				}else {
					bT.next = head;
					bT = head;
				}
			}
			//移动head
			head = next;
		}
		
		//分好区域以后,要把他们串起来
		if(sT != null) {
			sT = eH;
			eT = eT == null? sT : eT;
		}
		if(eT != null) eT.next = bH;
		
		//三个区域都有可能为空,因此这种方法返回是最简便的!!
		return sH != null ? sH : eH != null ? eH : bH;
	}
}

随即节点链表

在这里插入图片描述

1、哈希表,用一个 HashMap<Node,Node> 来存放原节点与拷贝节点。然后再连接好拷贝节点即可。

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);
	}

2、不适用哈希表,而是把复制的节点放在原节点之后。在这里插入图片描述

太聪明了!!!老链表还是指向老链表的节点,新链表指向新链表的节点。两者之间没有互相干扰!!!最后新老链表分开即可。

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;
	}

两个单链表

在这里插入图片描述

1、判断有无环:用哈希表;不用哈希表,就用快慢指针,如果有环则一定相遇,在相遇后,快指针回到 head 节点,由一次走两步变成一次走一步,最后快慢指针一定会在第一个如环节点相遇。

2、判断完有环无环后,如果无环,可以遍历第一条链表。并放入哈希表中,然后遍历第二条链表,查找是否出现过。如果有环,参照 1。如果不用 map ,就可以遍历第一条,统计该链的长度与记录 end1 节点,遍历第二条,统计该链长度与 end2 节点,比对 end1 与 end2 ,如果地址相同(注意这里不是值相同!!!),就说明他们相交,这里不可能分叉开!!!单链表!! 接着,需要获得第一个相交的节点,因此 先比较两个链表长度大小,先到同一个起点,(也就是 :假设 head1=100,head2 = 80,那么先让 head1 先走20步,达到同一起点,然后再一起走。他们一定会一起走到第一个相交的节点数。

3、如果一个链表有环,一个无环,绝不相交。

4、两个有环相交。三中情况!

在这里插入图片描述

怎么区分???

1、获取两个链表的头节点 head1,head2,和 入环节点 loop1,loop2。

2、如果 loop1 == loop2 (地址相同,不是值相同),那么就是第二种情况,那么要求第一个相交的节点就直接参考上面的方法。

3、让 loop1 一直往下走,如果都走回 loop1 了,就说明不相交,第一种情况,如果走着走着走到 loop2 了,说明相交,则返回 loop1 或者 loop2 都是对的!!!

public class FindFirstIntersectNode {
	
	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;
	}
	
	//判断是否有环,没有环就返回null,有环就返回入环节点
	public static Node getLoopNode(Node head) {
		
		if (head == null || head.next == null || head.next.next == null) {
			return null;
		}
		
		//找到共同位置
		Node quick = head.next.next;
		Node slow = head.next;
		while(quick != slow) {
			if(quick.next == null || quick.next.next == null) {
				return null;
			}
			quick = quick.next.next;
			slow = slow.next;
		}
		//快指针指回head,重新出发,一步一个next, 再次相遇就是入环节点
		quick = head;
		while(quick != slow) {
			quick = quick.next;
			slow = slow.next;
		}
		return quick;
	}
	
	//两条链表都没有环
	public static Node noLoop(Node head1, Node head2) {
		
		//遍历第一条
		Node cur = head1;
		int length1 = 0;
		while(cur.next != null) {
			length1++;
			cur = cur.next;
		}
		
		//遍历第二条
		Node cur2 = head2;
		int length2 = 0;
		while(cur2.next != null) {
			length2++;
			cur2 = cur2.next;
		}
		
		//此时的cur 与 cur2都指向了两条链表的最后
		//如果cur 与 cur2的地址不相同,就说明他们不相交了!
		if(cur != cur2) {
			return null;
		}else {
			//找到相交位置并返回
			int len = length1 - length2;
			cur = len > 0 ? head1 : head2;
			cur2 = cur == head1 ? head2 : head1;
			len = Math.abs(len);
			
			while(len != 0) {
				len--;
				cur = cur.next;
			}
			
			while(cur != cur2) {
				cur = cur.next;
				cur2 = cur2.next;
			}
		}
		return cur;
	}
	
	//如果两个都有环
	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 {
			//如果两个入环节点不相同,就让LOOP1一直走下去,如果回到loop1说明不相交,
			//如果到了loop2.则返回哪一个都是正确的。
			cur1 = loop1.next;
			while (cur1 != loop1) {
				if (cur1 == loop2) {
					return loop1;
				}
				cur1 = cur1.next;
			}
			return null;
		}
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_41960890/article/details/85764462