劍指offer刷題記錄(一)

我的解法如下: 

class Solution {
    public String reverseLeftWords(String s, int n) {
        Solution solution = new Solution();
        char value;
        char [] stringArr = s.toCharArray();//首先將String類型的s轉變爲char數組
        int length = stringArr.length;
        for (int i = 0; i <n ; i++) {
            value = stringArr[0];
            stringArr[0] = 0;
            solution.move(stringArr);
            stringArr[length-1] = value;
        }
        String out = String.valueOf(stringArr);
        return out;

    }
    public void move(char[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            if (arr[i + 1] != 0) {
                arr[i] = arr[i + 1];
            }else{
                arr[i] = 0;
                break;
            }
        }

    }
       
}

問題:速度慢,繁瑣,原始

這裏總結我這道題時出現的問題或者細節:

1.char和string的區別:

1. char是表示的是字符,定義的時候用單引號,只能存儲一個字符。例如; char='d'.  而String表示的是字符串,定義的時候用雙引號,可以存儲一個或者多個字符。例如:String=“we  are neuer”。

2. char是基本數據類型,而String是個類,屬於引用數據類型。String類可以調用方法,具有面向對象的特徵。

2.在靜態的方法中不能直接調用非靜態的方法或屬性

解決辦法:

1. 將被調用的方法設置成靜態方法;
2. new本類,然後通過實例來調用。

3.將字符串和字符串數組互相轉換方法

java可以使用兩種方法直接將字符數組轉爲字符串
方法1:直接在構造String時轉換。
char[] data = {‘a’, ‘b’, ‘c’};
String str = new String(data);
方法2:調用String類的方法轉換。
String.valueOf(char[] ch)

java可以使用兩種方法直接將字符串轉爲字符數組
情況一:如果是有分隔符的那種例如”abc,def,ghi”;就直接分割就行了.
String string = “abc,def,ghi”;
String [] strArr= string.split(“,”); //注意分隔符是需要轉譯
情況二:如果是”abcdefghijk”這種字符串,就直接
String string1 = “abcdefghijk” ;
char [] strArr1 = string1.toCharArray(); //注意返回值是char數組
 

我見到最簡單解法如下:

class Solution {
    public String reverseLeftWords(String s, int n) {
        String str1=s.substring(0,n);
        String str2=s.substring(n);
        StringBuilder sb=new StringBuilder();
        sb.append(str2).append(str1);
        return sb.toString();
    }
}

1.substring() 方法返回字符串的子字符串。 簡單說就是切割字符串

public String substring(int beginIndex) 或 public String substring(int beginIndex, int endIndex)

begin是包括,end不包括

2.StringBuilder類和StringBuffer類非常像,前者速度快,應用廣,但是後者在考慮線程安全的時候使用。

轉載一個別人總結的:https://blog.csdn.net/x83853684/article/details/82081658

 

2.

我的解法:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.lang.Math;

class Solution {
    public int[] printNumbers(int n) {
        List<Integer> list=new ArrayList<Integer>();
        for (int i = 1; i < Math.pow(10,n); i++) {
            list.add(i);
        }
        int[] outer = new int[list.size()];
        for (int i = 0; i <list.size() ; i++) {
            outer[i] = list.get(i);
        }
        return outer;
}
}

我這個可以優化的地方:普通數組是無法在創建之後,更改長度的,所以我選用了ArrayList,但是爲什麼我們不能在獲取n之後就計算出數組應該存入多少數據呢?

怎麼樣去避免使用Math.pow這個函數呢,下面的解法在時間大幅度提升。

class Solution {
    public int[] printNumbers(int n) {
        int sum = 9;
        while(n > 1){
            sum = sum * 10 + 9;
            n--;
        }
        int[] arr = new int[sum];
        for(; sum > 0;){
            arr[sum - 1] = sum--;
        }
        return arr;
    }
}

 

3.

這裏直接上題解:使用快慢指針

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode slow = head, fast = head;
        for (int i = 0; i < k; i++)
            fast = fast.next;
        
        while (fast != null) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
}

讓快指針先走K步,再讓他們倆同時走,這樣我們就能保證快指針擁有領先慢指針K步,所以當快指針走完時,慢指針就指向倒數第K個指針。

轉載一個快慢節點的應用:https://www.jianshu.com/p/21b4b8d7d31b

 

4.

總體難度不大,我的解法有點投機:

class Solution {
    public String replaceSpace(String s) {        
        return s.replace(" ", "%20");
    }
    
}

直接調用JAVA lang包下的replace方法,將空格替換成“%20”

看答案還有一種複雜一點:

 public String replaceSpace(String s) {
        StringBuilder sb = new StringBuilder();
		for(int i = 0;i<s.length();i++) {
			if(s.charAt(i) == ' ') {
				sb.append("%20");
			}else {
				sb.append(s.charAt(i));
			}
		 }
	     return sb.toString();

這裏補充一個打印數組的知識點:

三種打印數組的方法:1.for循環遍歷 2.增強for循環 3.調用Array.tostring()方法

https://blog.csdn.net/chenkaibsw/article/details/78989459

 

5.

答案一:

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null){
            return null;
        }
        TreeNode node = helper(root);
        return node;
    }
    public TreeNode helper(TreeNode root){
        if(root == null){
            return null;
        }
        TreeNode mid = root.left;//中轉來儲存你的左節點
        root.left = helper(root.right);
        root.right = helper(mid);
        return root;
    }
}

這個非常善用遞歸,在對樹(指針實現)進行編程的時候,因爲層層往下的結構都是一樣的,所以遞歸在裏面就非常地好用。

答案二是參考一個大佬,分別前中後和層次遍歷來解答這題,不使用遞歸。

核心代碼:

TreeNode temp = node.left;
node.left = node.right;
node.right = temp;

前序遍歷非遞歸

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if (root == null){
            return null;
        }
        TreeNode node = root;
        Deque<TreeNode> stack = new LinkedList<>();
        while (node != null || !stack.isEmpty()){
            if (node != null){
                // 交換左右子樹
                inventTreeNode(node);
                // 原先的先序遍歷
                stack.push(node);
                node = node.left;
            }else {
                node = stack.pop();
                node = node.right;
            }
        }
        return root;
    }
    
    private void inventTreeNode(TreeNode node){
        TreeNode temp = node.left;
        node.left = node.right;
        node.right = temp;
    }
}

這裏的Deque是雙向隊列,可以直接實現Stack和Queue兩種數據結構的功能。具體看下面這個網址:

https://blog.csdn.net/u013967628/article/details/85210036

中序遍歷非遞歸

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if (root == null){
            return null;
        }
        TreeNode node = root;
        Deque<TreeNode> stack = new LinkedList<>();
        while (node != null || !stack.isEmpty()){
            if (node != null){
                stack.push(node);
                node = node.left;
            }else {
                node = stack.pop();
                // 交換左右子樹
                inventTreeNode(node);
                // 這裏因爲交換了,不能繼續往右子樹走,因爲現在的右子樹,是原來的左子樹
                // 所以接下來要走左邊
                node = node.left;
            }
        }
        return root;
    }
    
    private void inventTreeNode(TreeNode node){
        TreeNode temp = node.left;
        node.left = node.right;
        node.right = temp;
    }
}

後序遍歷非遞歸

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if (root == null){
            return null;
        }
        TreeNode node = root;
        TreeNode tempNode = null;// 記錄上次訪問的節點
        Deque<TreeNode> stack = new LinkedList<>();
        while (node != null || !stack.isEmpty()){
            if (node != null){
                stack.push(node);
                node = node.left;
            } else {
                node = stack.peek();
                if (node.right == null || node.right == tempNode){
                    // 右節點爲空,或者右節點已經被訪問過
                    // 當前節點出棧
                    tempNode = stack.pop();
                    // 交換
                    inventTreeNode(node);
                    // 這裏要置空,不然node還是存在,又會往左子樹去了
                    node = null;
                }else {
                    node = node.right;
                }
            }
        }
        return root;
    }
    
    private void inventTreeNode(TreeNode node){
        TreeNode temp = node.left;
        node.left = node.right;
        node.right = temp;
    }
}

層次遍歷非遞歸

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null){
            return null;
        }
        TreeNode node = root;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(node);
        while(!queue.isEmpty()){
            node = queue.poll();
            // 翻轉左右子樹
            inventTreeNode(node);
            if(node.left != null){
                queue.offer(node.left);
            }
            if(node.right != null){
                queue.offer(node.right);
            }
        }
        return root;
    }
    
    private void inventTreeNode(TreeNode node){
        TreeNode temp = node.left;
        node.left = node.right;
        node.right = temp;
    }
}

 

6.

方法一:使用雙指針來解決這個問題。解題思路看下圖:

申請兩個指針,第一個指針叫 pre,最初是指向 null 的。
第二個指針 cur 指向 head,然後不斷遍歷 cur。
每次迭代到 cur,都將 cur 的 next 指向 pre,然後 pre 和 cur 前進一位。
都迭代完了(cur 變成 null 了),pre 就是最後一個節點了。

代碼如下:

class Solution {
	public ListNode reverseList(ListNode head) {
		//申請節點,pre和 cur,pre指向null
		ListNode pre = null;
		ListNode cur = head;
		ListNode tmp = null;
		while(cur!=null) {
			//記錄當前節點的下一個節點
			tmp = cur.next;
			//然後將當前節點指向pre
			cur.next = pre;
			//pre和cur節點都前進一位
			pre = cur;
			cur = tmp;
		}
		return pre;
	}
}

方法二:使用遞歸,遞歸理解確實非常困難

三個問題:1.這個函數是用來解決什麼的 2.遞歸調用的結束條件是什麼 3.尋求一個等式關係

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
	public ListNode reverseList(ListNode head) {
		//遞歸終止條件是當前爲空,或者下一個節點爲空
		if(head==null || head.next==null) {
			return head;
		}
		//這裏的cur就是最後一個節點
		ListNode cur = reverseList(head.next);
		//這裏請配合動畫演示理解
		//如果鏈表是 1->2->3->4->5,那麼此時的cur就是5
		//而head是4,head的下一個是5,下下一個是空
		//所以head.next.next 就是5->4
		head.next.next = head;
		//防止鏈表循環,需要將head.next設置爲空
		head.next = null;
		//每層遞歸函數都返回cur,也就是最後一個節點
		return cur;
	}
}

7.

我的解法:2 ms

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] reversePrint(ListNode head) {
        Deque<Integer> stack = new LinkedList<>();
        while(head != null){
            stack.push(head.val);
            head = head.next;
        }
        int [] outer = new int[stack.size()];
        int i =0;
        while(!stack.isEmpty()){
            outer[i++] = stack.pop();
        }
        return outer;

    }
}

我這個方法簡單明瞭,先弄一個棧,利用棧先入後出的特點,將一個一個節點的數值壓棧,然後再將棧裏的值一一出棧就是我們所需要的數組。但這裏需要兩個循環,因爲首先要入棧然後再出棧。

 

方法二:一個大神的答案,使用的遞歸   1ms

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    ArrayList <Integer> arrays = new ArrayList<>();
    public int[] reversePrint(ListNode head) {
        //遞歸的結束條件:head == null       
        recur(head);
        int [] outer = new int[arrays.size()];
        for(int i=0;i<outer.length;i++){
            outer[i] = arrays.get(i);
        }
        return outer;
    }
    public void recur(ListNode head){
        if(head == null){
            return;
        }
        recur(head.next);
        arrays.add(head.val);
    }
}

將head.next編程遞推函數的參數,定位到鏈表最後一位。所以這裏arrays.add(head.val)添加順序並不是從頭到尾,而是從尾到頭。添加完畢後只需要將其輸出就行。

這裏當時還發生了一個錯誤,我沒有將ArrayList <Integer> arrays = new ArrayList<>();這一行寫到reversePrint方法外,想想爲什麼不這麼做就報錯?報錯行是arrays.add(head.val);

 

8.

做了半天,沒做出來。貼出別人的解法

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dum = new ListNode(0), cur = dum;
        while(l1 !=null &&l2 !=null){
            if(l1.val >= l2.val){
                cur.next = l2;
                l2=l2.next;
                cur = cur.next;
                
            }else{
                cur.next = l1;
                l1=l1.next;
                cur = cur.next;
            }
        }
        if(l1 !=null){
            cur.next = l1;

        }else{
            cur.next = l2;
        }
        return dum.next;
    }
}

看了大佬思路,我完成的代碼,思路如下:

這裏我有2個問題,值得以後反覆琢磨:

1.爲什麼return dum.next;就可以把整條鏈條都給返回?

2.僞頭結點的意義?

 

9.

class CQueue {
    Deque<Integer> stack1,stack2;
    public CQueue() {
        stack1 = new LinkedList<Integer>();
        stack2 = new LinkedList<Integer>();

    }
    
    public void appendTail(int value) {        
             stack1.push(value);
}
    
    public int deleteHead() {
        if(!stack2.isEmpty()){
            return stack2.pop();
        }else if (!stack1.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
            return stack2.pop();
        }else{
            return -1;
        }
    }
}

解題思路:簡而言之就是stack2作爲改變stack1順序的一個工具,因爲比如輸入123,stack1裏面就是321,這時候輸出肯定是不對的,這就不是隊列了,所以需要Stack2來倒序,繼續變成123,這時候再輸出就行。

10.

解法一:主要思路就是用位運算,n&1,相當於n的最右邊一位和1做位運算,當然如果最右邊一位是1,得到1反之則爲0 .

然後需要將n右移,Java中有兩個右移“>>”(有符號)和“>>>”(無符號)兩種。

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int count = 0;
        while(n != 0){
            int tmp = n & 1;
            if(tmp  == 1){
                count++;
            }
            n >>>= 1;
        }
        return count;
    }
}

解法二:一個大佬的思路非常巧妙

public class Solution {
    public int hammingWeight(int n) {
        int res = 0;
        while(n != 0) {
            res++;
            n &= n - 1;
        }
        return res;
    }
}

思路如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章