leetcode旋轉五部曲


這裏總結leetcode中關於“旋轉的題目”。
前兩個“旋轉”的含義相同,後面的三個含義有所不同

  • 旋轉鏈表
  • 旋轉數組
  • 旋轉字符串
  • 旋轉數字
  • 旋轉圖像

1. 旋轉鏈表

題目描述

給定一個鏈表,旋轉鏈表,將鏈表每個節點向右移動 k 個位置,其中 k 是非負數。

示例 1:

輸入: 1->2->3->4->5->NULL, k = 2
輸出: 4->5->1->2->3->NULL
解釋:
向右旋轉 1 步: 5->1->2->3->4->NULL
向右旋轉 2 步: 4->5->1->2->3->NULL
示例 2:

輸入: 0->1->2->NULL, k = 4
輸出: 2->0->1->NULL
解釋:
向右旋轉 1 步: 2->0->1->NULL
向右旋轉 2 步: 1->2->0->NULL
向右旋轉 3 步: 0->1->2->NULL
向右旋轉 4 步: 2->0->1->NULL

思路

其實就是拿出鏈表後邊一段,放到前面。

至於後邊哪一段,就是倒數第k個節點開始往後。這裏出現了鏈表的常見問題:倒數第幾個的問題

這個問題有兩種方法,一是容易想到的,求倒數第k個不就是正數第n-k+1(n是鏈表長,從1開始計數)個嘛。
二是鏈表中的快慢指針法,讓兩個指針一直保持k個距離,當後一個指針到末尾時,前一個指針剛好指到倒數第k個位置。

我這裏就演示第一種容易的吧。爲啥不做第二種?因爲我懶唄,哎呀媽呀腦瓜疼,腦瓜疼,看見code腦瓜疼…

本題需要找到倒數第k個節點的前驅,然後才能從倒數第k處斷開,再拼接到頭部。

Java代碼

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        //特殊情況:鏈表爲空或者長度爲1
        if(head == null || head.next == null) return head;
        //計算鏈表長度
        int len = 1;
        ListNode p = head;
        while(p.next!=null){
            len++;
            p = p.next;
        }
        //此時p指向尾節點
        
        // 右移動k與右移k%len相同
        k = k%len;
        if(k == 0) return head;// 沒有移動,直接返回
        
        // 原問題等價於找出倒數第k個節點,然後斷開插入到頭部
        // 倒數第k個等於正數第Len-k個(從0開始計數)
        
        // 而要想從第k個斷開,就要找到它的的前驅,就是正數第Len-k-1個(從0開始計數)
        int cu = 0;
        ListNode q = head;
        while(cu!=len-k-1){
            cu++;
            q = q.next;
        }
        // 斷開鏈表,並拼接
        ListNode lnew = q.next;
        q.next = null;
        p.next = head;
        return lnew;
        
    }
}

2. 旋轉數組

題目描述

給定一個數組,將數組中的元素向右移動 k 個位置,其中 k 是非負數。

示例 1:

輸入: [1,2,3,4,5,6,7] 和 k = 3
輸出: [5,6,7,1,2,3,4]
解釋:
向右旋轉 1 步: [7,1,2,3,4,5,6]
向右旋轉 2 步: [6,7,1,2,3,4,5]
向右旋轉 3 步: [5,6,7,1,2,3,4]
示例 2:

輸入: [-1,-100,3,99] 和 k = 2
輸出: [3,99,-1,-100]
解釋:
向右旋轉 1 步: [99,-1,-100,3]
向右旋轉 2 步: [3,99,-1,-100]
說明:

儘可能想出更多的解決方案,至少有三種不同的方法可以解決這個問題。
要求使用空間複雜度爲 O(1) 的原地算法。

思路一

與旋轉鏈表不同的是,數組不能一下子拿下來半段放到前面,因爲題目要求空間複雜度爲 O(1) 。
因此最直接的做法,一個個移動。
每次將最後一個元素記錄下來記爲temp,讓它之前的元素一個個賦值給後面一個,最後把temp賦值給nums[0]。
重複這個過程k次,就是k次旋轉啦

Java代碼(1)

class Solution {
    public void rotate(int[] nums, int k) {
    //考慮特殊情況
        if(nums == null||nums.length == 0||nums.length == 1) return;
        int len = nums.length;
        if(k%len == 0) return;
        while(k>0){
            int temp = nums[len-1];//記錄最後一個元素
            //從前向後移動一步
            for(int i = len-1;i>0;i--){
                nums[i] = nums[i-1];
            }
            //將第一個元素放到開頭
            nums[0] = temp;
            k--;
        }
        
    }
}

思路二

先整體反轉
再前半段反轉
最後後半段反轉

Java代碼(2)

//從索引start 到end反轉一個數組
  private void reverse(int[] nums, int start, int end) {
        while (start < end) {
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start++;
            end--;
        }
    }

class Solution {
    public void rotate(int[] nums, int k) {
    //考慮特殊情況
        if(nums == null||nums.length == 0||nums.length == 1) return;
        int len = nums.length;
        if(k%len == 0) return;
        reverse(nums,0,len-1);
    	reverse(nums,0,k-1);
        reverse(nums,k,n-1);
    }
}

3. 旋轉字符串

題目描述

給定兩個字符串, A 和 B。

A 的旋轉操作就是將 A 最左邊的字符移動到最右邊。 例如, 若 A = ‘abcde’,在移動一次之後結果就是’bcdea’ 。如果在若干次旋轉操作之後,A 能變成B,那麼返回True。

示例 1:
輸入: A = ‘abcde’, B = ‘cdeab’
輸出: true

示例 2:
輸入: A = ‘abcde’, B = ‘abced’
輸出: false
注意:

A 和 B 長度不超過 100。

思路

如果A通過旋轉能變成B,那麼連續拼接的兩個A包含了A的所有旋轉情況,也肯定包含B。

因此,拼接兩個A,如果B在兩個A的連續拼接後的字符串中,則返回true;否則,返回false

Java代碼

class Solution {
    public boolean rotateString(String A, String B) {
        //特殊情況
        if(A == null&&B==null) return true;
        else if(A == null &&B!=null) return false;
        else if(B == null && A!=null) return false;
        int lena = A.length();
        int lenb = B.length();
        if(lena!=lenb) return false;//長度要相等
        
        //拼接兩個A
        StringBuilder sb = new StringBuilder();
        sb.append(A);
        sb.append(A);
        
        //如果B在兩個A的連續拼接後的字符串中,則返回true,否則,返回false
        if(sb.toString().contains(B)) return true;
        else return false;
    }
}

4. 旋轉數字

題目描述

我們稱一個數 X 爲好數, 如果它的每位數字逐個地被旋轉 180 度後,我們仍可以得到一個有效的,且和 X 不同的數。要求每位數字都要被旋轉。

如果一個數的每位數字被旋轉以後仍然還是一個數字, 則這個數是有效的。0, 1, 和 8 被旋轉後仍然是它們自己;2 和 5 可以互相旋轉成對方;6 和 9 同理,除了這些以外其他的數字旋轉以後都不再是有效的數字。

現在我們有一個正整數 N, 計算從 1 到 N 中有多少個數 X 是好數?

示例:
輸入: 10
輸出: 4
解釋:
在[1, 10]中有四個好數: 2, 5, 6, 9。
注意 1 和 10 不是好數, 因爲他們在旋轉之後不變。

思路

關鍵是理解題意,什麼是“好數”呢?就是每一位旋轉180度後,仍是一個數,且和原來不同
既然要求每一位,那就是要對數字進行常見的“逐位剝離”了,這個方法不難,且在題中常用到,要記住。

然後就是判斷“好數”了。

一位數就這10個:0 1 2 3 4 5 6 7 8 9
滿足旋轉180度後仍是一個數的有0 1 2 5 6 8 9
滿足旋轉180度仍是一個數,但和原來相同的是0 1 8
滿足旋轉180度仍是一個數,且和原來不同的只有2 5 6 9
而剩下的 3 4 7,兩個條件都不滿足。

因此,只要遇到某一位是 3 4 7的一個,則該數肯定不是好數,直接返回false
如果遇到 2 5 6 9,則說明該位是滿足兩個條件的,繼續判斷其他位。
而遇到某一位 0 1 8這三個數,既不能說明該數不是“好數”(其他位有可能滿足“好數條件”),也不能說明它是“好數”(該位不滿足“好數”條件),也就是不改變對該數的判斷,所以不需要處理它們。

實際上默認該數不是“好數”,即flag=false,就是對這種情況的處理。如果一個數的某位有0 1 8 只有靠其他位來“拯救”該數了,如果其他位滿足條件,則改變flag爲true,否則,就爲false。

Java代碼

class Solution {
    public int rotatedDigits(int N) {
        int count = 0;
        //從1到N逐個判斷
        for(int i = 1;i<=N;i++){
            if(isGoodNum(i)) count++;
        }
        return count;
    }
    //判斷一個數字是否是“好數”
    public boolean isGoodNum(int x){
        boolean flag = false;
        while(x!=0){
            int t = x%10;
            if(t == 3|| t == 4 || t== 7) return false;
            else if(t == 2|| t==5||t==6||t == 9) flag = true;
            x = x/10;
        }
        if(flag) return true;
        else return false;
    }
}

5. 旋轉圖像

題目描述

給定一個 n × n 的二維矩陣表示一個圖像。

將圖像順時針旋轉 90 度。

說明:

你必須在原地旋轉圖像,這意味着你需要直接修改輸入的二維矩陣。請不要使用另一個矩陣來旋轉圖像。

示例 1:

給定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],

原地旋轉輸入矩陣,使其變爲:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
示例 2:

給定 matrix =
[
[ 5, 1, 9,11],
[ 2, 4, 8,10],
[13, 3, 6, 7],
[15,14,12,16]
],

原地旋轉輸入矩陣,使其變爲:
[
[15,13, 2, 5],
[14, 3, 4, 1],
[12, 6, 8, 9],
[16, 7,10,11]
]

思路一

順時針旋轉90度:其實就是將二維數組,先上下交換,再沿對角線(\)交換。

Java代碼(1)

class Solution {
    //交換二維數組兩個點
    public void swap(int[][] matrix,int x1,int y1,int x2,int y2){
        int temp = matrix[x1][y1];
        matrix[x1][y1] = matrix[x2][y2];
        matrix[x2][y2] = temp;
    }
    
    public void rotate(int[][] matrix) {
        int len = matrix.length;
        //上下交換
        for(int i=0;i<len/2;i++){
            for(int j=0;j<len;j++){
                swap(matrix,i,j,len-i-1,j);
            }
        }
        //沿對角線交換
        for(int i=0;i<len;i++){
            for(int j=i;j<len;j++){
                swap(matrix,i,j,j,i);
            }
        }
    }
}

思路二

兩層循環,逐個移動,每次交換四個點。這個不好記啊,那些索引搞得我暈暈的。還是思路一簡單易懂。

Java代碼(2)

class Solution {
	public void rotate(int[][] matrix) {
	        int len = matrix.length;
	        for(int i=0;i<len/2;i++){
	            int start = i;
	            int end = len-i-1;
	            for(int j = 0;j<end-start;j++){
	                //交換
	                int temp = matrix[start][start+j];
	                matrix[start][start+j] = matrix[end-j][start];
	                matrix[end-j][start] = matrix[end][end-j];
	                matrix[end][end-j] = matrix[start+j][end];
	                matrix[start+j][end] = temp;
	            }
	    }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章