左神算法讲堂笔记 04 宏观调控和链表

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

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值