1、绕圈打印矩阵
public ArrayList<Integer> printMatrix(int[][] matrix) {
ArrayList<Integer> list = new ArrayList<>();
// 给定左上角和右下角,进行打印
int tr = 0, tc = 0, dr = matrix.length-1, dc = matrix[0].length-1;
while (tr <= dr && tc <= dc) {
rotateEdge(list,matrix,tr++,tc++,dr--,dc--);
}
return list;
}
public void rotateEdge(ArrayList<Integer> list, int[][] matrix,int tr,int tc,int dr,int dc) {
if (tr == dr) {
for (int i = tc; i <= dc; i++) {
list.add(matrix[tr][i]);
}
} else if (tc == dc) {
for (int i = tr; i <= dr; i++) {
list.add(matrix[i][tc]);
}
}else {
int curC = tc, curR = tr;
while (curC < dc) {
list.add(matrix[tr][curC++]);
// System.out.print(matrix[tr][curC++] + " ");
}
while (curR < dr) {
list.add(matrix[curR++][dc]);
// System.out.print(matrix[curR++][dc] + " ");
}
curC = dc; curR = dr;
while (curC > tc) {
list.add(matrix[dr][curC--]);
// System.out.print(matrix[dr][curC--] + " ");
}
while (curR > tr) {
list.add(matrix[curR--][tc]);
// System.out.print(matrix[curR--][tc] + " ");
}
}
}
2、顺时针旋转矩阵
package zcy;
import java.util.ArrayList;
public class ExchangeMatrix {
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);
}
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 rotate(int[][] matrix) {
// 给定左上角和右下角,进行打印
int tr = 0 ,tc = 0,dc = matrix[0].length-1, dr = matrix.length-1 ;
while (tr < dr) {
rotateEdge(matrix,tr++,tc++,dr--,dc--);
}
}
public static void rotateEdge(int[][] matrix,int tr,int tc,int dr,int dc) {
for (int i = 0; i < dr-tr; i++) {
int temp = matrix[tr][tc+i];
matrix[tr][tc + i] = matrix[dr - i][tc]; // 1 = 4
matrix[dr - i][tc] = matrix[dr][dc - i]; // 4 = 3
matrix[dr][dc - i] = matrix[tr + i][dc]; // 3 = 2
matrix[tr + i][dc] = temp; // 2 = tmp
}
}
}
3、之字形打印
“之”字形打印矩阵
【题目】 给定一个矩阵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)
解法
依然是宏观调控。
子函数的功能是:给出矩阵两个点的坐标,打印两个点之间连线位置的值。通过标志位指明从下到上,还是从上到下打印。
宏观上:控制两个点的连线是对角线
package zcy;
public class zhiPrintMatrix {
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);
}
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 rotate(int[][] matrix) {
// 给定左上角和右下角,进行打印
int cc = matrix[0].length-1, cr = matrix.length-1;
int tr = 0 ,tc = 0,dc = 0, dr = 0 ;
int up = 1;
while (tr == 0 || (tr>0 && tr >= dr)) {
rotateEdge(matrix,tr,tc,dr,dc,up);
up = -up;
if (tr != cr) {
tr++;
}else{
tc++;
}
if (dc != cc) {
dc++;
}else{
dr++;
}
}
}
public static void rotateEdge(int[][] matrix,int tr,int tc,int dr,int dc, int up) {
if(up > 0){
// tr -> dr
while (tr >= dr) {
System.out.println(matrix[tr--][tc++]);
}
}else{
while (dr <= tr) {
System.out.println(matrix[dr++][dc--]);
}
}
}
}
4、链表
笔试中:越快过题越好
面试:空间更少,时间保持o(n)
4.1 判断一个链表是不是回文链表
最容易写的方法是,读一遍然后全部加到栈里面,读第二遍再依次弹出栈进行比对。
比较此的方法,空间o(n/2),一个快指针一个慢指针,保证快指针走到底时(不一定到底,无关紧要)慢指针恰好走到一半。这样依据上半段进行判断下半段。
额外空间为o(1)的方法是: 控制慢指针指向中点, 然后把后半段进行链表反转。接着从两头进行比对。 最后记得反转回去。
public static boolean isPalindrome3(Node node) {
Node n1 = node,n2 = node;
while (n2.next!=null &&n2.next.next != null) {
n1 = n1.next;
n2 = n2.next.next;
}
// 为了找到n1为mid中点
Node mid = n1;
System.out.println("mid: " + n1.val);
// n1 -> n2
n2 = n1.next;
Node dummy = new Node();
dummy.next = n1;
//逆转链表
while (n2 != null) {
n1.next = n2.next;
n2.next = dummy.next;
dummy.next = n2;
n2 = n1.next;
}
n2 = dummy.next;
n1 = node;
// while (n2 != null) {
// System.out.println("逆序:"+ n2.val);
// n2 = n2.next;
// }
// System.out.println("mid: " + mid.val);
mid.next = null;//中间点放空
dummy.next = n2; // dummy指向右端的头,方便复原
boolean flag = true;
while (n1.next != null) {
if (n1.val != n2.val) {
flag = false;
break;
}
n1 = n1.next;
n2 = n2.next;
}
// 还原
n1 = dummy.next;
n2 = n1.next;
while (n2 != null) {
n1.next = n2.next;
n2.next = dummy.next;
dummy.next = n2;
n2 = n1.next;
}
return flag;
}
@Test
public void testForHuiWen() {
Node node = new Node();
node.val = 1;
Node node2 = new Node();
node2.val = 2;
Node node3 = new Node();
node3.val = 3;
Node node4 = new Node();
node4.val = 4;
Node node5 = new Node();
node5.val = 3;
Node node6 = new Node();
node6.val =2;
Node node7 = new Node();
node7.val = 1;
node.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = node6;
node6.next = node7;
System.out.println(isPalindrome2(node));
}
4.2 依据num划分链表
题意:把链表划分为,前半段小于num,中间段等于num,后一段大于num。没小段内的相对位置保持不变。
解法:构造三个链表,小链表,等链表,大链表。
找到第一个小于num的点,那么这里往后遇到的 小于num 的点都要加到 小链表之后。
同理构造出其余两个链表,最后把三个链表拼接。
解法
public Node eChange(Node head, int num) {
Node sl = null, eq = null, bg = null, next = head;
Node cs = null, ce = null, cb = null;
while (head != null) {
// 要用next把head 的下一位保存起来,
// 把head和整条链表隔离
// 否则走不动
next = head.next;
head.next = null;
if (head.val < num) {
if (sl == null) {
sl = head;
cs = head;
}else{
cs.next = head;
cs = head;
}
}
else if (head.val == num) {
if (eq == null) {
eq = head;
ce = head;
}else {
ce.next = head;
ce = head;
}
}
else {
if (bg == null) {
bg = head;
cb = head;
}else{
cb.next = head;
cb = head;
}
}
head = next;
}
System.out.println(sl.val + " " + cs.val + " : " + eq.val + " " + ce.val + " : "
+ bg.val + " "+cb.val);
if (cs != null) {
cs.next = eq;
ce = ce == null? cs : ce;
}
if (ce != null) {
ce.next = bg;
}
while (sl != null) {
System.out.println(sl.val);
sl = sl.next;
}
return sl != null ? sl : eq != null ? eq : bg;
}
@Test
public void testForHuiWen() {
Node node = new Node();
node.val = 1;
Node node2 = new Node();
node2.val = 2;
Node node3 = new Node();
node3.val = 3;
Node node4 = new Node();
node4.val = 4;
Node node5 = new Node();
node5.val = 3;
Node node6 = new Node();
node6.val =2;
Node node7 = new Node();
node7.val = 1;
node.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = node6;
node6.next = node7;
eChange(node,3);
// System.out.println(eChange(node, 3));
// System.out.println(isPalindrome2(node));
}
4.3 复杂链表的赋值
原则: 赋值后的链表和原来的链表必须在内存上完全隔离,不可掺杂。
解法:
解法一:用map存储每个节点的copy节点,最后把copy节点串起来。
解法二:第一步:原链表连上其copy节点,这样把链表长度扩大2倍。 第二步:让copy节点的random指向cur.random.next(random位置的copy节点) 第三步:分离
解法一
static public RandomListNode Clone(RandomListNode pHead)
{
Map<RandomListNode, RandomListNode> map = new HashMap<>();
RandomListNode cur = pHead, clone;
while (cur != null) {
clone = new RandomListNode(cur.label);
clone.next = cur.next;
clone.random = cur.random;
map.put(cur, clone);
cur = cur.next;
}
cur = pHead;
while (cur != null) {
clone = map.get(cur);
clone.random = map.get(cur.random);
clone.next = map.get(cur.next);
cur = cur.next;
}
clone = map.get(pHead);
// while (clone!= null) {
// System.out.println(clone.label + " " + clone.random.label);
//
// clone = clone.next;
// }
return clone;
}
解法二
public RandomListNode Clone(RandomListNode pHead){
if(pHead == null)
return null;
RandomListNode cur = pHead,next;
// 每个节点后面加上一个copy节点
while (cur != null) {
next = cur.next;
cur.next = new RandomListNode(cur.label);
cur.next.next = next;
cur = next;
}
// copy节点的random指向random的copy节点
cur = pHead;
RandomListNode copy;
while (cur != null) {
next = cur.next.next;
copy = cur.next;
copy.random = cur.random == null ? null :cur.random.next;
cur = next;
}
// 分离
cur = pHead;
RandomListNode res = cur.next;
while (cur != null) {
next = cur.next.next;
cur.next.next = next == null ? null : next.next;//拷贝节点
cur.next = next;
cur = next;
}
// while (res != null) {
// System.out.println(res.label + " " + res.random.label);
// res = res.next;
// }
return res;
}
4.4 链表是否有环
快指针一次走两步,慢指针一次走一步,如果快指针走到为空,那么无环。
如果有环,那么快指针和慢指针必定相遇。相遇的时候,让快指针回到原点,并且一次走一步,那么相遇的那个点,就是环的入口。
4.5 两条链表相交的第一个位置
如果两个链表都无环:
得到两个链表的len1、len2和最后一个位置end1、end2。如果end1 != end2 那么两条链表不可能相交(链表后面只能连一个)。如果end1 == end2 ,那么假设len1 - len2 = 20, 那么链表1先走20步, 然后再一起走,就能得到两个链表相交的第一个位置。
如果一个有环一个无环:
那么两个链表不可能相交
如果两个都有环,有三种情况:
根据两个链表,分别得到其 loop1 、loop2 (第一个环的位置,通过4.3),end1、end2 。
如果end1 != end2 : 第一种情况, 那么不相交
如果loop1 == loop2 : 第二种情况, 那么套用无环的逻辑,求head到loop的长度,然后长度大的先走,然后再一起走,返回第一个相遇的位置。
如果loop1 != loop2 : 那么返回哪个都算正确答案。
package zcy;
import org.junit.Test;
import java.util.*;
class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
public class Main {
public static void main(String[] args) {
}
@Test
public void test() {
// 1->2->3->4->5->6->7->null
ListNode head1 = new ListNode(1);
head1.next = new ListNode(2);
head1.next.next = new ListNode(3);
head1.next.next.next = new ListNode(4);
head1.next.next.next.next = new ListNode(5);
head1.next.next.next.next.next = new ListNode(6);
head1.next.next.next.next.next.next = new ListNode(7);
// 0->9->8->6->7->null
ListNode head2 = new ListNode(0);
head2.next = new ListNode(9);
head2.next.next = new ListNode(8);
head2.next.next.next = head1.next.next.next.next.next; // 8->6
System.out.println(FindFirstCommonNode(head1, head2).val);
// 1->2->3->4->5->6->7->4...
head1 = new ListNode(1);
head1.next = new ListNode(2);
head1.next.next = new ListNode(3);
head1.next.next.next = new ListNode(4);
head1.next.next.next.next = new ListNode(5);
head1.next.next.next.next.next = new ListNode(6);
head1.next.next.next.next.next.next = new ListNode(7);
head1.next.next.next.next.next.next = head1.next.next.next; // 7->4
// 0->9->8->2...
head2 = new ListNode(0);
head2.next = new ListNode(9);
head2.next.next = new ListNode(8);
head2.next.next.next = head1.next; // 8->2
System.out.println(FindFirstCommonNode(head1, head2).val);
// 1->2->3->4->5->6->7->4...
// 0->9->8->6->4->5->6..
head2 = new ListNode(0);
head2.next = new ListNode(9);
head2.next.next = new ListNode(8);
head2.next.next.next = head1.next.next.next.next.next; // 8->6
System.out.println(FindFirstCommonNode(head1, head2).val);
}
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) {
return null;
}
ListNode loop1 = getLoop(pHead1), loop2 = getLoop(pHead2);
if(loop1 == null && loop2 == null){
return noLoop(pHead1, pHead2);
} else if (loop1 != null && loop2 != null) {
return BothLoop(loop1, pHead1, loop2, pHead2);
}
return null;
}
public ListNode goNextByStep(ListNode p, int step) {
for (int i = 0; i < step; i++) {
p = p.next;
}
return p;
}
public ListNode getSame(ListNode p1, ListNode p2) {
while (p1 != p2) {
p1 = p1.next;
p2 = p2.next;
}
return p1;
}
public ListNode getLoop(ListNode pHead) {
if (pHead == null ||pHead.next == null || pHead.next.next == null) {
return null;
}
ListNode f = pHead.next.next, s = pHead.next;
while (f != s) {
if (f.next == null || f.next.next == null) {
return null;
}
s = s.next;
f = f.next.next;
}
f = pHead;
while (f != s) {
f = f.next;
s = s.next;
}
return f;
}
public ListNode BothLoop(ListNode loop1, ListNode pHead1, ListNode loop2, ListNode pHead2) {
if (loop1 == loop2) {
ListNode cur1 = pHead1 , cur2 = pHead2;
int n = 0;
while (cur1.next != loop1) {
n++;
cur1 = cur1.next;
}
while (cur2.next != loop1) {
n--;
cur2 = cur2.next;
}
if (cur2 == cur1) {
cur1 = n > 0 ? pHead1 : pHead2;
cur2 = cur1 == pHead1 ? pHead2 : pHead1;
n = Math.abs(n);
cur1 = goNextByStep(cur1,n);
return getSame(cur1, cur2);
}
return null;
}else{
ListNode cur1 = loop1;
while (cur1.next != loop1) {
if(cur1 == loop2){
return cur1;
}
cur1 = cur1.next;
}
return null;
}
}
public ListNode noLoop(ListNode pHead1, ListNode pHead2) {
ListNode cur1 = pHead1 , cur2 = pHead2;
int n = 0;
while (cur1.next != null) {
n++;
cur1 = cur1.next;
}
while (cur2.next != null) {
n--;
cur2 = cur2.next;
}
if (cur2 == cur1) {
cur1 = n > 0 ? pHead1 : pHead2;
cur2 = cur1 == pHead1 ? pHead2 : pHead1;
n = Math.abs(n);
cur1 = goNextByStep(cur1,n);
return getSame(cur1, cur2);
}
return null;
}
}