四丶矩阵和链表的打印

LeetCode 59:螺旋矩阵

class Solution {
    public int[][] generateMatrix(int n) {
        int l = 0, r = n - 1, t = 0, b = n - 1;
        int[][] mat = new int[n][n];
        int num = 1, tar = n * n;
        while(num <= tar){
            for(int i = l; i <= r; i++) mat[t][i] = num++; // left to right.
            t++;
            for(int i = t; i <= b; i++) mat[i][r] = num++; // top to bottom.
            r--;
            for(int i = r; i >= l; i--) mat[b][i] = num++; // right to left.
            b--;
            for(int i = b; i >= t; i--) mat[i][l] = num++; // bottom to top.
            l++;
        }
        return mat;
    }
}

1.转圈打印矩阵

给定一个整型矩阵matrix,请按照转圈的方式打印它。 例如:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
打印结果为:1,2,3,4,8,12,16,15,14,13,9, 5,6,7,11, 10
【要求】 额外空间复杂度为O(1)。
思路;始终确定两个点。没有打印框中的左上角的点和右下角的点。然后直到这两个点重合的时候,那么这个矩阵就打印完成了。

public class quanPrint {

    //其中aR,aC:代表左上角的行和列。bR和bC:代表的是右下角的行和列。
        public static void spiralOrderPrint(int[][] matrix){
            if(matrix==null||matrix.length==0||matrix.length==1&&matrix[0].length==0){
                throw new RuntimeException("矩阵为空");
            }
            int aR=0;
            int aC=0;
            int bR=matrix.length-1;
            int bC=matrix[0].length-1;
            while (aR<bR){
                printedge(matrix,aR++,aC++,bR--,bC--);
            }
        }
        public static void printedge(int[][] m,int aR,int aC,int bR,int bC){
            if (aR==bR){
                for (int i=aC;i<=bC;i++){
                    System.out.printf(m[aR][i]+" ");
                }
            }
            else if (aC==bC){
                for (int i=aR;i<=bR;i++){
                    System.out.print(m[i][aC]+" ");
                }
            }else{
                int curC=aC;
                int curR=aR;
                while(curC!=bC){
                    System.out.print(m[aR][curC]+" ");
                    curC++;
                }
                while(curR!=bR){
                    System.out.print(m[curR][bC]+" ");
                    curR++;
                }
                while(curC!=aC){
                    System.out.print(m[bR][curC]+" ");
                    curC--;
                }
                while (curR!=aR){
                    System.out.print(m[curR][aC]+" ");
                    curR--;
                }
            }
        }

        public static void main(String[] args) {
            int[][] m={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};
            spiralOrderPrint(m);
        }
}

2:旋转90度打印正方形矩阵

给定一个整型正方形矩阵matrix,请把该矩阵调整成 顺时针旋转90度的样子。 【要求】 额外空间复杂度为O(1)。
下图的错误:应将打印修改成 -----旋转
在这里插入图片描述
结果如下:
在这里插入图片描述
思路:因为我们要旋转打印,所以首先将原数组中相应的位置进行交换,成最后的样子的数组,然后在依次打印该数组。
注意:这种交换位置的操作,始终都是先降低一个位置给temp,然后倒序来赋值。这样就能实现交换的操作。
注意:旋转的几个点的确定:
1.首先确定最初的四个点的位置
第一个[aR][aC]
第二个m[bR][aC]
第三个m[bR][bC]
第四个m[aR][bC]
2.然后在上面的基础上在进行变化。

 public class e09rotate {
            public static void rotate(int[][] matrix){
                int aR=0;
                int aC=0;
                int bR=matrix.length-1;
                int bC=matrix[0].length-1;
                while(aR<bR){
                    rotateEdge(matrix,aR++,aC++,bR--,bC--);
                }
                printMatrix(matrix);
            }
            //依次交换位置。到最终需要实现的数组。
            public static void rotateEdge(int[][] m,int aR,int aC,int bR,int bC){
                int times=bC-aC;  //需要旋转的次数。
                int tmp=0;
                for (int i=0;i<times;i++){
                    tmp=m[aR][aC+i];  //后者:4个点中的第一个点
                    m[aR][aC+i]=m[bR-i][aC]; //后者:4个点中的最后一个点。
                    m[bR-i][aC]=m[bR][bC-i];  //后者:4个点的第三个点
                    m[bR][bC-i]=m[aR+i][bC];  //后者:4个点的第2个点。
                    m[aR+i][bC]=tmp;
                }
            }
            public static void printMatrix(int[][] m){
                for (int i=0;i<m.length;i++){
                    for (int j=0;j<m[0].length;j++){
                        System.out.print(m[i][j]+" ");
                    }
                    System.out.println();
                }
            }
            public static void main(String[] args) {
                int[][] m={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};
                rotate(m);
            }
        }

3.z字形(之字形 )打印矩阵

在这里插入图片描述
思路:每次的打印都以A和B的位置为参考,但是每次打印:(1)如果是从上往下打印,以a为起点。(2)如果是从下往上打印,以b为起点。

public class ZPrint {
        public static void printMatrixZigZag(int[][] matrix){
            int aR=0;
            int aC=0;
            int bR=0;
            int bC=0;
            int EndR=matrix.length-1;
            int EndC=matrix[0].length-1;
            boolean fromUp=false;
            while(aR!=EndR+1){  //a来到最后一行,因为a和b是同时到达最后一行的。
                printLevel(matrix,aR,aC,bR,bC,fromUp);
                aR= aC==EndC?aR+1:aR;  //只有a的列数来到最后一列,a的行数才增加
                aC= aC==EndC?aC:aC+1;
                bC= bR==EndR?bC+1:bC;
                bR= bR==EndR?bR:bR+1;  //上面的四行,就是a和b分别移动到下一个位置。
                fromUp=!fromUp;
            }
        }

        public static void printLevel(int[][] m,int aR,int aC,int bR,int bC,boolean fromUp){
            if(aR==0&&bR==0){
                System.out.print(m[aR][aC]+" ");
                return;
            }
            if (fromUp){
                while (aR<=bR){  //等价于while(ar<=br)
                    System.out.print(m[aR++][aC--]+" "); //从上往下打印,以a为起点
                }
            }else{
                while(bR>=aR){     //等价于while(br>=ar )
                    System.out.print(m[bR--][bC++]+" ");  //从下往上打印,以b为起点。

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

        }
}

LeetCode .Z字形变化

在这里插入图片描述

class Solution {
    public String convert(String s, int numRows) {
        if (numRows == 1) return s;  //只有一行直接返回
        boolean isDown =false;
        int currRow=0;
         List<StringBuilder> rows = new ArrayList<StringBuilder>();
       for (int i = 0; i < Math.min(numRows, s.length()); i++){
            rows.add(new StringBuilder());
            }
        for(int i =0;i<s.length();i++){
            rows.get(currRow).append(s.charAt(i));
              if(currRow==0||currRow==numRows-1){
                  
                  isDown=!isDown;
              }
                  currRow+=isDown?1:-1;
                
            }
        StringBuilder res = new StringBuilder();
        for(StringBuilder row:rows){
            res.append(row);
        }
        return res.toString();
    }
}

4.在行列都排好序的矩阵中找数

在这里插入图片描述
在这里插入图片描述
思路:从右上角(或者从左小角)出发,如果当前值比k大,就往左走,比k小,往下走。

public class e11FindNumInSortedMatrix {
    public static boolean isContains(int[][] matrix,int K){
        int row=0;
        int col=matrix[0].length-1;
        while(row<matrix.length&&col>=0){
            if(matrix[row][col]==K){
                return true;
            }else if (matrix[row][col]>K){
                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 = 234;
        System.out.println(isContains(matrix, K));
    }
}

5.打印两个链表的公共部分

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

public class e11PrintCommonPart {
    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) {
        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 head){
        System.out.print("LinkedList: ");
        while (head!=null){
            System.out.print(head.value+" ");
            head=head.next;
        }
        System.out.println();
    }

    public static void main(String[] args) {
        Node node1 = new Node(2);
        node1.next = new Node(3);
        node1.next.next = new Node(5);
        node1.next.next.next = new Node(6);

        Node node2 = new Node(1);
        node2.next = new Node(2);
        node2.next.next = new Node(5);
        node2.next.next.next = new Node(7);
        node2.next.next.next.next = new Node(8);

        printLinkedList(node1);
        printLinkedList(node2);
        printCommonPart(node1, node2);
    }
}

6.判断链表是否是回文结构。

在这里插入图片描述
思路:使用栈,先将链表压入栈中,然后从栈中弹出来,然后与原链表依次比较。

import java.util.Stack;

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

方法2:先找中点,然后将中点之后的链表全部压入到栈中,然后将栈弹出,与前面的链表进行比较。
注意:这里不分奇数和偶数,如果是奇数中间的那个节点不参与比较。
在这里插入图片描述

class Solution {
  public boolean isPalindrome(ListNode head) {
      if(head==null||head.next ==null){
          return true;
      }
      ListNode slow  =head,fast = head.next;
      Stack<ListNode> stack = new Stack<ListNode>();
      while(fast!=null&& fast.next!=null){
          slow = slow.next;
          fast = fast.next.next;
      }
      ListNode cur = slow.next;
      while(cur!=null){
          stack.push(cur);
          cur = cur.next;
      }
      while(!stack.isEmpty()){
          if(stack.pop().val!= head.val){
              return false;
          }
          head = head.next;
      }
      return true;
  }
}

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

【题目】 给定一个单向链表的头节点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的节点即可。对某部分内部的节点顺序不做要求。

注意:在笔试中可使用方案1,面试中可使用方案2
思路:方案1:将链表全部压入到数组中,然后使用荷兰国旗问题去解决。(但是这种快排的方式不能解决稳定性。)

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数组Partition
		arrPartition(nodeArr, pivot);
        //变成链表
		for (i = 1; i != nodeArr.length; i++) {
			nodeArr[i - 1].next = nodeArr[i];
		}
		nodeArr[i - 1].next = null;
		return nodeArr[0];//返回链表的head
	}
 
	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) {//如果小于p,
				swap(nodeArr, ++small, index++);//首先交换small和index的位置,然后各自+1,表示
			} 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;
	}

方案2:
在这里插入图片描述
注意:遵守一个原则,先连接上,然后在根据条件进行截取
在取链表上的结点的时候,
1.先取出next,保存: Node next=head.next;
2.然后在将head后面的值断开: head.next = null;
3.最后在进行移位:head = next;

public class NO4_7 {
        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

            while(head!=null){
                Node 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){
                if(eT!=null){
                    sT.next=eH;
                    eT.next =bH;
                }else{
                    sT.next=bH;
                }

            }
            return sH!=null?sH:eH!=null?eH:bH;
        }

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

        public static void main(String[] args) {
            Node head1 = new Node(7);
            head1.next = new Node(9);
            head1.next.next = new Node(1);
            head1.next.next.next = new Node(8);
            head1.next.next.next.next = new Node(5);
            head1.next.next.next.next.next = new Node(2);
            head1.next.next.next.next.next.next = new Node(5);
            printLinkedList(head1);
            // head1 = listPartition1(head1, 4);
            head1 = listPartition1(head1, 5);
            printLinkedList(head1);

        }
        public static class Node{
            int value;
            Node next;
            public Node(int value){
                this.value = value;
            }
        }

}

8.复制含有随机指针节点的链表

在这里插入图片描述
思路:借助HashMap的特性,定义一个HashMap<Node,Node> map对象,key和value都是value类型 ,key来存旧的链表,value来存新的链表。最后利用老节点和新节点的对应关系来实现一个拷贝新的链表。

 public static Node copyListWithRand1(Node head){
            HashMap<Node, Node> map = new HashMap<Node, Node>();
            Node curr =head;
            while(curr!=null){
                map.put(curr,new Node(curr.getValue()));
                curr=curr.next;
            }
            Node curr =head;
            while(curr!=null){
                map.get(curr).next = map.get(curr.next);
                map.get(curr).rand = map.get(curr.rand);
                curr= curr.next;
            }
            return map.get(head);
        }

方法2:
在这里插入图片描述

   public static Node copyListWithRand(Node head){
           Node cur =head;
           Node next =null;
           while(cur!=null){
               next = cur.next; //先把以前的关系记录一下。
               cur.next=new Node(cur.getValue());
               cur.next.next = next==null?null:next;
               cur =next;
           }
           cur =head;
           Node curCopy=null;
           while(cur!=null){
               next =cur.next.next;
               curCopy =cur.next;
               curCopy.rand=cur.rand==null?null:cur.rand.next;
               cur = next;
           }
           Node res =head.next;
           cur =head;
           while(head!=null){
               next =cur.next.next;
               curCopy=cur.next;
               cur.next=next;
               curCopy.next=next==null?null:next.next;
               cur=next;
           }
           return res;
       }

9.两个单链表相交的一系列问题

注意: 下面的判断链表有无环的方法有错,最好参考我笔记中的LeetCode的141,和142.
在这里插入图片描述

1)如何判断一个单链表有环还是无环

方法一:

  遍历链表元素,放入HashSet集合中

方法二:

  准备俩个指针,一个快指针,一个慢指针,快指针一次走俩步,慢指针一次走一步

  如果有环,快指针和慢指针一定会相遇

  当出现相遇情况,快指针回到起始节点,并且由一次走俩步变成一次走一步

  快指针和慢指针一定会在第一个入环节点相遇

2)如何判断俩个无环链表第一个相交节
在这里插入图片描述

方法一:

  将遍历第一个链表,放入map中,遍历第二个链表,判断map中是否有这个元素

方法二:

  遍历链表一,统计链表一的长度len1以及或者链表一最后一个节点end1

  遍历链表二,统计链表二的长度len2以及或者链表二最后一个节点end2

  比较end1和end2内存地址是否相等,如果end1不等于end2,则不可能相交

  如果相等,比较len1和len2长度,假如len1=100,len2=80,链表一先走20步。。。。

  如果一个链表有环,一个链表无环,不可能相交

3)如何判断俩个有环链表第一个相交节点

1.俩个环不相交

2.先相交后共享一个环

如图:
在这里插入图片描述

准备工作:

head1,loop1(相交的结点)

head2,loop2

如果loop1等于loop2,属于结构2

如果loop1不等于loop2,可能是结构1或者结构3

代码如下:


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 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;
    }
    //两个无环链表的相交问题
    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;
    }

    //两个有环链表的相交问题,loop:表示入环的结点
    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; //这里返回loop1和loop2都对,只是离哪个链表更近而已。
                }
                cur1 = cur1.next;
            }
            return null;
        }
    }

    public static void main(String[] args) {
        // 1->2->3->4->5->6->7->null
        Node head1 = new Node(1);
        head1.next = new Node(2);
        head1.next.next = new Node(3);
        head1.next.next.next = new Node(4);
        head1.next.next.next.next = new Node(5);
        head1.next.next.next.next.next = new Node(6);
        head1.next.next.next.next.next.next = new Node(7);

        // 0->9->8->6->7->null
        Node head2 = new Node(0);
        head2.next = new Node(9);
        head2.next.next = new Node(8);
        head2.next.next.next = head1.next.next.next.next.next; // 8->6
        System.out.println(getIntersectNode(head1, head2).value);

        // 1->2->3->4->5->6->7->4...
        head1 = new Node(1);
        head1.next = new Node(2);
        head1.next.next = new Node(3);
        head1.next.next.next = new Node(4);
        head1.next.next.next.next = new Node(5);
        head1.next.next.next.next.next = new Node(6);
        head1.next.next.next.next.next.next = new Node(7);
        head1.next.next.next.next.next.next = head1.next.next.next; // 7->4

        // 0->9->8->2...
        head2 = new Node(0);
        head2.next = new Node(9);
        head2.next.next = new Node(8);
        head2.next.next.next = head1.next; // 8->2
        System.out.println(getIntersectNode(head1, head2).value);

        // 0->9->8->6->4->5->6..
        head2 = new Node(0);
        head2.next = new Node(9);
        head2.next.next = new Node(8);
        head2.next.next.next = head1.next.next.next.next.next; // 8->6
        System.out.println(getIntersectNode(head1, head2).value);

    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值