我要进大厂-算法-第四天(拿下链表)

阅读该文章需要60分钟

一:旋转正方形矩阵

1.1:题目描述

旋转正方形矩阵
【题目】 给定一个整型正方形矩阵matrix,请把该矩阵调整成顺时针旋转90度的样子。
【要求】 额外空间复杂度为O(1)。

1.2:详细思路
		/**
		 *		左上角
		 *		↓
		 * 		1	2	3	4
		 *
		 * 		5	6	7	8
		 *
		 * 		9	10	11	12
		 *
		 * 		13	14	15	16
		 *					↑
		 *					右下角
		 */
		 如上图所示这是一个正方形矩阵
		 很直观的可以想到,正方形里里外外有几圈,我们需要把这几圈的值都旋转
		 拿最外圈来说,我们只需要 1、2、3 旋转到 4、8、12的位置,
		 同理4、8、12旋转到16、15、14的位置,以此类推
		 我们扣边界,就可以写出1->4、2->8、3->12
		 转完最外圈,我们让圈缩小,也就是,左上角向右下角移动一格位置,右下角向左上角移动一个位置
		 让我们看下code即可
1.3:Java代码
package xyz.fudongyang.basic.class_03.my;

public class Code_06_RotateMatrix {

	public static void rotate(int[][] matrix) {
		int tR = 0;
		int tC = 0;
		int dR = matrix.length - 1;
		int dC = matrix[0].length - 1;
		while (tR <= dR && tC <= dC){
			// 转一圈
			rotateEdge(tR++,tC++,dR--,dC--,matrix);
		}
	}

	private static void rotateEdge(int tR,int tC,int dR,int dC,int[][] matrix){
		// 转几次 n-1 次
		int times = dR - tR;
		for (int i = 0; i < times; i++) {
			// 先把这个第一个位置记录下来
			int temp = matrix[tR][tC+i];
			// 左下 替代 左上
			matrix[tR][tC+i] = matrix[dR-i][tC];
			// 右下 替代 左下
			matrix[dR-i][tC] = matrix[dR][dC-i];
			// 右上 替代 右下
			matrix[dR][dC-i] = matrix[tR+i][dC];
			// 左上 替代 右上
			matrix[tR+i][dC] = temp;
		}
	}




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

	}

}

二:反转单向链表、反转双向链表

2.1:题目描述

反转单向和双向链表
【题目】 分别实现反转单向链表和反转双向链表的函数。
【要求】 如果链表长度为N,时间复杂度要求为O(N),额外空间复杂度要求为O(1)

说实话,不要认为这个题目比较简单就不去做,我花了3个小时才做出来。

2.2:思路讲解

反转单向链表

反转单向链表
拿一个链表
1->2->3->4
反转链表的每个节点分为几步?
拿1节点开说

首先我们全局定义一个Node,作为反转后链表的头节点,那么初始化应该为null

我们需要把 链表中1的位置断开,也就是这样 1  2->3->4
我们拿到1这个节点,1这个节点的需要放到反转链表的头节点之前,也就是 1->null
我们的反转链表的头节点就是1了,然后遍历后续的2->3->4

我们再反转下2节点,把链表的头节点(现在是2节点)位置断开 2   3->4
我们拿到2这个节点,2这个节点的需要放到反转链表的头节点之前,也就是 2->1->null
我们的反转链表的头节点就是2了,然后遍历后续的3->4

以此类推

反转双向链表

反转双向链表
拿一个链表
1->2->3->4
反转链表的每个节点分为几步?
拿1节点开说

首先我们全局定义一个Node,作为反转后链表的头节点,那么初始化应该为null

我们需要把 链表中1的位置断开,也就是这样 1  2->3->4
1的父节点为null,1的后续节点应该为反转链表的头节点,也就是null
反转链表的头节点的父节点应该是1,这里反转链表的头节点为null就不用设置了
此时,反转链表的结构如下 1->null,继续遍历后续的2->3->4

我们需要把 链表中2的位置断开,也就是这样 2  3->4
2的父节点为null,2的后续节点应该为反转链表的头节点,也就是1
反转链表的头节点的父节点应该是2
此时,反转链表的结构如下 2<->1->null,继续遍历后续的 3->4
以此类推
2.3:Java代码

package xyz.fudongyang.basic.class_03.my;

import java.util.zip.CRC32;

public class Code_07_ReverseList {

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

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

    private static Node reverseList(Node node) {
        // 新链表
        Node pre = null;
        // 临时变量
        Node next = null;
        while (node != null){
            // 记录一下我的 原来链后面的值,不能破坏我的循环规则
            next = node.next;
            // 我要放到新链表的第一个位置
            node.next = pre;
            // 我就代表新链表了
            pre = node;
            // 行了,继续循环吧
            node = next;
        }
        return pre;
    }

    private static Node reverser(Node node){

        Node newNode = null;
        while (node != null){
            Node next = node.next;
            node.next = newNode;
            newNode = node;
            node = next;
        }
        return newNode;
    }

    public static class DoubleNode{
        private int value;
        private DoubleNode pre;
        private DoubleNode next;

        public DoubleNode(int value){
            this.value = value;
        }
    }

    public static DoubleNode reverseList(DoubleNode doubleNode){
        DoubleNode newNode = null;
        DoubleNode allNext;
        while (doubleNode != null){
            // 拿出当前节点
            allNext = doubleNode.next;
            // 当前节点的下一个指向新链表
            doubleNode.next = newNode;
            if (newNode != null){
                // 新链表的头节点指向当前节点
                newNode.pre = doubleNode;
            }
            // 当前节点就是新节点
            doubleNode.pre = null;
            newNode = doubleNode;
            // 遍历后续节点
            doubleNode = allNext;
        }
        return newNode;
    }


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

    public static void printDoubleLinkedList(DoubleNode head) {
        System.out.print("Double Linked List: ");
        DoubleNode end = null;
        while (head != null) {
            System.out.print(head.value + " ");
            end = head;
            head = head.next;
        }
        System.out.print("| ");
        while (end != null) {
            System.out.print(end.value + " ");
            end = end.pre;
        }
        System.out.println();
    }

    public static void main(String[] args) {
        Node head1 = new Node(1);
        head1.next = new Node(2);
        head1.next.next = new Node(3);
        printLinkedList(head1);
        head1 = reverseList(head1);
        printLinkedList(head1);

        DoubleNode head2 = new DoubleNode(1);
        head2.next = new DoubleNode(2);
        head2.next.pre = head2;
        head2.next.next = new DoubleNode(3);
        head2.next.next.pre = head2.next;
        head2.next.next.next = new DoubleNode(4);
        head2.next.next.next.pre = head2.next.next;
        printDoubleLinkedList(head2);
        printDoubleLinkedList(reverseList(head2));

    }

}

三: 之字形打印矩阵

3.1:题目描述

“之”字形打印矩阵
【题目】 给定一个矩阵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)。

3.2:思路讲解
大体观
不要被题目吓到,也就是求边界的问题,仔细debug一下边界值就行了
我可以讲讲我的思路吗?

上图↘
		/**
		 *		左上角     右上角
		 *		↓           ↓
		 * 		1	2	3	4
		 *
		 * 		5	6	7	8
		 *
		 * 		9	10	11	12
		 *		↑			↑
		 *	  左下角		右下角
		 */
		 也就是打印对角线呗,(1)、(2、5)、(9、6、3)、(4、7、10)、(11、8)、(12)
		 那行,我只需要分为两步就行了
		 第一步;打印对角线
		 第二布:调整对角线
		 举个例子,我们打印(1)、(2、5)、(9、6、3)这三个说明一下
		 
		 第一步:打印对角线
		 两点确定一条直线,那我们确认对角线需要两个点,右上角的点的坐标,左下角点的坐标。
		 还有箭头的方向,我们仔细观察可以得知方向如下:左下->右上  右上->左下 左下->右上 ····
		 我们打印对角线可以做到,只要给我们对角线两端的的坐标,和打印方向即可。
		 
		 第二步:调整对角线
		 右上角的对角线的坐标一直向右移动,如果移动到最后一列,那就向下移动
		 左下角的对角线的坐标一直向下移动,如果移动到最后一行,那就向左移动
		 一直移动到最后一个位置为止。
		 
		 看code
3.3:Java代码
package xyz.fudongyang.basic.class_03.my;

public class Code_08_ZigZagPrintMatrix {

	public static void printMatrixZigZag(int[][] matrix) {

		/**
		 *		左上角
		 *		↓
		 * 		1	2	3	4
		 *
		 * 		5	6	7	8
		 *
		 * 		9	10	11	12
		 *					↑
		 *					右下角
		 */
		int tR = 0;
		int tC = 0;
		int dR = 0;
		int dC = 0;
		int endR = matrix.length-1;
		int endC = matrix[0].length-1;
		boolean flag = false;
		// 如果右上角还没越界,就继续走
		while (tR != endR +1){
			// 打印对角线的值
			printLevel(matrix,tR,tC,dR,dC,flag);
			// 改变 tR,tC,dR,dC边界值
			tR = tC == endC ? tR+1 : tR;
			tC = tC == endC ? tC : tC+1;
			dC = dR == endR ? dC+1 : dC;
			dR = dR == endR ? dR : dR+1;
			// 反过来继续循环,为了下次反过来打印对角线的值
			flag = !flag;
		}
		System.out.println();
	}

	private static void printLevel(int[][] matrix,int tR,int tC,int dR,int dC,boolean flag){
		// 从右上到左下
		if (flag){
			while (tR != dR +1){
				System.out.print(matrix[tR++][tC--]+" ");
			}
		}else {
			while ((dR != tR -1)){
				System.out.print(matrix[dR--][dC++]+" ");
			}
		}
	}


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

	}

}

四:行和列都排好序,找数

4.1:题目描述

在行列都排好序的矩阵中找数
【题目】 给定一个有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)。

4.2:思路讲解
哈哈哈 这个题 我没刷算法之前就会 可简单

举个例子:

        第一个判断的数
        ↓
    1 2 3
    4 5 6
    7 8 9
        ↑
        最后一个判断的数
        
    我们先从最后一列判断,最后一列是改行的最大值,如果比最后一列的第一行大,
    那么就和下一行比较,如果比最后一列的该行小,那就向该行的左移动。
    直到向左向下移动不了,那就是不存在该数。
    
    看code
4.3:Java代码
package xyz.fudongyang.basic.class_03.my;

public class Code_09_FindNumInSortedMatrix {

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

	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(isContains(matrix, K));
	}

}

五:打印两个有序链表的公共部分

5.1:题目描述

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

5.2:思路讲解
不难,如果感觉没思路,可能题意没看懂
其实让你求的是这两个有序链表中的值,有哪些相同的

很简单
同时遍历两个链表,如果哪个链表的当前值小,哪个链表向后移动一下
如果两个链表的当前值一样大,则打印,并两个链表同时向后移动一下

看code
5.3:Java代码
package xyz.fudongyang.basic.class_03.my;

public class Code_10_PrintCommonPart {

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

	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.1:题目描述

判断一个链表是否为回文结构
【题目】 给定一个链表的头节点head,请判断该链表是否为回文结构。

例如: 1->2->1,返回true。 1->2->2->1,返回true。15->6->15,返回true。 1->2->3,返回false。

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

6.2:思路如下

空间复杂度O(N)

将链表中的值压到栈
那么出栈循序则是链表的逆序
那么链表的逆序和链表的正序对比
如果相等,则是回文结构,你品品这个道理吧,一会让你看代码

空间复杂度O(N/2)

找到链表的中间位置
将链表中间位置之后的数压倒栈中
这样我们只压入一半的数据,也就是2/N的数据
然后将栈弹出的数和链表的数做对比

空间复杂度O(1)

找到链表的中间位置
反转链表后面的位置
对比链表前面的数据,和中间以后反转链表的数据
得出对比结果后,还原源数据(Java值传递,我们不能改变原始数据)
6.3:Java代码
package xyz.fudongyang.basic.class_03.my;

import java.util.Stack;

public class Code_11_IsPalindromeList {

    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;  // is mid
        Node n2 = head;
        while (n2.next != null && n2.next.next != null){
            n1 = n1.next;
            n2 = n2.next.next;
        }

        // reverse mid->last
        n2 = n1.next;
        n1.next = null;
        // temp Node
        Node n3;
        while (n2 != null){
            n3 = n2.next;
            n2.next = n1;
            n1 = n2;
            n2 = n3;
        }
        // verified value
        // save n1 node
        n3 = n1;
        n2 = head;
        boolean res = true;
        while (n1 != null && n2 != null){
            if (n1.value != n2.value){
                res = false;
                break;
            }
            n1 = n1.next;
            n2 = n2.next;
        }

        // recover data
        n1 = n3.next;
        n3.next = null;
        while (n1 != null){
            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();
    }

    public static void main(String[] args) {

        Node head = null;
        printLinkedList(head);
        System.out.print(isPalindrome1(head) + " | ");
        System.out.print(isPalindrome2(head) + " | ");
        System.out.println(isPalindrome3(head) + " | ");
        printLinkedList(head);
        System.out.println("=========================");

        head = new Node(1);
        printLinkedList(head);
        System.out.print(isPalindrome1(head) + " | ");
        System.out.print(isPalindrome2(head) + " | ");
        System.out.println(isPalindrome3(head) + " | ");
        printLinkedList(head);
        System.out.println("=========================");

        head = new Node(1);
        head.next = new Node(2);
        printLinkedList(head);
        System.out.print(isPalindrome1(head) + " | ");
        System.out.print(isPalindrome2(head) + " | ");
        System.out.println(isPalindrome3(head) + " | ");
        printLinkedList(head);
        System.out.println("=========================");

        head = new Node(1);
        head.next = new Node(1);
        printLinkedList(head);
        System.out.print(isPalindrome1(head) + " | ");
        System.out.print(isPalindrome2(head) + " | ");
        System.out.println(isPalindrome3(head) + " | ");
        printLinkedList(head);
        System.out.println("=========================");

        head = new Node(1);
        head.next = new Node(2);
        head.next.next = new Node(3);
        printLinkedList(head);
        System.out.print(isPalindrome1(head) + " | ");
        System.out.print(isPalindrome2(head) + " | ");
        System.out.println(isPalindrome3(head) + " | ");
        printLinkedList(head);
        System.out.println("=========================");

        head = new Node(1);
        head.next = new Node(2);
        head.next.next = new Node(1);
        printLinkedList(head);
        System.out.print(isPalindrome1(head) + " | ");
        System.out.print(isPalindrome2(head) + " | ");
        System.out.println(isPalindrome3(head) + " | ");
        printLinkedList(head);
        System.out.println("=========================");

        head = new Node(1);
        head.next = new Node(2);
        head.next.next = new Node(3);
        head.next.next.next = new Node(1);
        printLinkedList(head);
        System.out.print(isPalindrome1(head) + " | ");
        System.out.print(isPalindrome2(head) + " | ");
        System.out.println(isPalindrome3(head) + " | ");
        printLinkedList(head);
        System.out.println("=========================");

        head = new Node(1);
        head.next = new Node(2);
        head.next.next = new Node(2);
        head.next.next.next = new Node(1);
        printLinkedList(head);
        System.out.print(isPalindrome1(head) + " | ");
        System.out.print(isPalindrome2(head) + " | ");
        System.out.println(isPalindrome3(head) + " | ");
        printLinkedList(head);
        System.out.println("=========================");

        head = new Node(1);
        head.next = new Node(2);
        head.next.next = new Node(3);
        head.next.next.next = new Node(2);
        head.next.next.next.next = new Node(1);
        printLinkedList(head);
        System.out.print(isPalindrome1(head) + " | ");
        System.out.print(isPalindrome2(head) + " | ");
        System.out.println(isPalindrome3(head) + " | ");
        printLinkedList(head);
        System.out.println("=========================");

    }

}

七:放到最后

在这里插入图片描述

一群lsp,赶紧学习去

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值