【精選】JAVA算法題(十八)

一、移除鏈表中指定的節點

題目:

/**
 * 刪除鏈表中等於給定值 val 的所有節點。
 *
 * 示例:
 * 輸入: 1->2->6->3->4->5->6, val = 6
 * 輸出: 1->2->3->4->5
 */

題的描述很簡單,做起來並不容易,有一些小坑,要注意的就是頭尾的節點的處理和連續相等的情況處理,其它的就非常簡單了,遍歷鏈表,將與val相等的前一個節點和下一節點相連。

處理這樣的問題肯定會用到while循環,這樣更方便也更不容易出錯。

因爲要處理頭結點就是目標節點的情況,所以你可以在開頭就加上一個while循環來過濾

在循環中你可以檢測下一個節點的val是否等於目標值,如果相等就while判斷下一個節點的val是不是目標,直到找到不是的那一個,連接之前的節點,爲此你要定義一個局部變量方便操作

在其中你還要注意加上判斷以防止尾節點的問題

    public static ListNode method1(ListNode head, int val) {
        while (head!=null&&head.val==val){
            head=head.next;
        }
        if (head==null){
            return null;
        }
        ListNode operation=head;
        while (operation.next!=null){
            if (operation.next.val==val){
                if (operation.next.next==null){
                    operation.next=null;
                }else {
                    ListNode transientNode=operation.next;
                    while (transientNode!=null&&transientNode.val==val){
                        transientNode=transientNode.next;
                    }
                    if (transientNode==null){
                        operation.next=null;
                    }else {
                        operation.next=transientNode;
                        operation=transientNode;
                    }
                }
            }else {
                operation=operation.next;
            }

        }
        return head;
    }

這樣的代碼很長很繁瑣,還有一堆判斷,我們是否可以有一個更好的辦法來優化算法

你要考慮頭結點的問題,這個問題你可以自己給它加上一個頭結點,這樣就不用再爲頭結點的處理加一個循環了

你要考慮連續結點值都是目標值的問題,你可以採用這樣的策略,如果下一個節點是目標值,那麼就連接next.next,否則向前移動。這樣的話即使是連續目標值也能得到判斷

    public static ListNode method2(ListNode head,int val){
        ListNode header = new ListNode(-1);
        header.next = head;
        ListNode cur = header;
        while(cur.next != null){
            if(cur.next.val == val ){
                cur.next = cur.next.next;
            }else{
                cur = cur.next;
            }
        }
        return header.next;
    }

這樣一看代碼是不是就減少了很多了,思路也很清晰,幾乎所有的while循環的問題我們都可以用遞歸解決

我們要關注的問題就是當前節點的val值是不是目標值,如果是就返回next,不是就返回本身

    public static ListNode method3(ListNode head,int val){
        if(head == null) return null;
        head.next = method3(head.next, val);
        return head.val == val ?  head.next : head;
    }

    public static class ListNode {
      int val;
      ListNode next;
      ListNode(int x) { val = x; }
    }

是不是思路更爲清晰了呢?

二、質數的數量

題目:

/**
 * 統計所有小於非負整數 n 的質數的數量。
 *
 * 示例:
 * 輸入: 10
 * 輸出: 4
 * 解釋: 小於 10 的質數一共有 4 個, 它們是 2, 3, 5, 7 。
 */

入門的時候肯定學過如何判斷一個數是不是質數,也學過一些優化,那就一個一個判斷嘛,所以就寫下了這段代碼

    public static int method1(int n) {
        int count=0,j=0,k=0;
        for (int i=2;i<=n;i++){
            k=(int)sqrt(i+1);
            for (j=2;j<=k;j++){
                if (i%j==0){
                    break;
                }
            }
            if (j==k+1){
                count++;
            }
        }
        return count;
    }

然而事情並沒有那麼簡單,它是有時間限制的,給出了一個5000000的大數,我就懵了,仔細研究,一定有什麼訣竅

寫下20個數,仔細觀察,你會發現質數的倍數都不是質數(很傻缺的結論),但是你就可以用這個結論來屆這道題,如果你檢測到3是一個質數,那麼之後的6,9,12,15,18.。。。。就都不是質數了,都不用判斷了,這就節省了非常多的判斷。

    public static int method2(int n){
        boolean[] isPrime = new boolean[n];
        for (int i = 2; i < n; i++) {
            isPrime[i] = true;
        }
        for (int i = 2; i * i < n; i++) {
            if (!isPrime[i]) continue;
            for (int j = i * i; j < n; j += i) {
                isPrime[j] = false;
            }
        }
        int count = 0;
        for (int i = 2; i < n; i++) {
            if (isPrime[i]) count++;
        }
        return count;
    }

三、同構字符串

題目:

/**
 * 給定兩個字符串 s 和 t,判斷它們是否是同構的。
 * 如果 s 中的字符可以被替換得到 t ,那麼這兩個字符串是同構的。
 * 所有出現的字符都必須用另一個字符替換,同時保留字符的順序。兩個字符不能映射到同一個字符上,但字符可以映射自己本身。
 *
 * 示例 1:
 * 輸入: s = "egg", t = "add"
 * 輸出: true
 *
 * 示例 2:
 * 輸入: s = "foo", t = "bar"
 * 輸出: false
 *
 * 示例 3:
 * 輸入: s = "paper", t = "title"
 * 輸出: true
 */

滿足同構字符串有一個關鍵的條件,那就是字符與字符之間必須是一對一的關係,不能一對多也不能多對一。那我們就使用HashMap建立這樣的一對一的映射關係,最後遍歷字符串看看是否對應正確。

    public static boolean method1(String s, String t) {
        if (s.length()!=t.length())return false;
        HashMap<Character,Character> hashMap=new HashMap<>();
        char[] chars=s.toCharArray();
        char[] chart=t.toCharArray();
        for (int i=0;i<chars.length;i++){
            if (!hashMap.keySet().contains(chars[i])&&hashMap.values().contains(chart[i])){
                return false;
            }
            hashMap.put(chars[i],chart[i]);
        }
        for (int i=0;i<chars.length;i++){
            if (!hashMap.get(chars[i]).equals(chart[i])){
                return false;
            }
        }
        return true;
    }

這樣要遍歷字符串兩遍,可以優化一下,使它遍歷一遍。要完成的任務爲一對一的字符映射,藉助HashMap,可以先檢測鍵中是否含有該字符,含有的話取出來看看映射的是否正確,沒有的話看看值中是否含有對應字符,含有就說明對應關係錯誤。這樣一遍遍歷就搞定了。

    public static boolean method2(String s, String t) {
        if (s.length()!=t.length())return false;
        HashMap<Character,Character> hashMap=new HashMap<>();
        char[] chars=s.toCharArray();
        char[] chart=t.toCharArray();
        for (int i=0;i<chars.length;i++){
            if (hashMap.keySet().contains(chars[i])){
                if (!hashMap.get(chars[i]).equals(chart[i])){
                    return false;
                }
            }else {
                if (hashMap.values().contains(chart[i])){
                    return false;
                }else {
                    hashMap.put(chars[i],chart[i]);
                }
            }
        }
        return true;
    }

還能更爲優化一下,我們是藉助HashMap來完成字符的一對一映射的,HashMap的底層是使用數組實現的,而字符剛好可以轉換爲Ascll碼當做數組下標來使用,核心思想還是一樣,但這樣性能上就更爲提升了一截。

    public static boolean method4(String s,String t){
        char[] sc = s.toCharArray();
        char[] tc = t.toCharArray();
        char[] map = new char[256];
        for (int i = sc.length-1;i >= 0;i--) {
            if (map[sc[i]] != map[tc[i]+128]) {
                return false;
            }
            map[sc[i]] = map[tc[i] + 128] = sc[i];
        }
        return true;
    }

更換一種思路,兩個字符串相互判斷,對兩個結果使用與連接。不在內部處理一對一,而是簡單的檢查多對一,相互判斷之後的結果也是一對一。

    public static boolean method3(String s,String t){
        return isIsomorphic(s,t) && isIsomorphic(t,s);
    }

    private static boolean isIsomorphic(String t, String s) {
        char[] dict = new char[256];
        Arrays.fill(dict, (char) 0);
        for (int i = 0; i < s.length(); i++) {
            if (dict[s.charAt(i)] == (char) 0) {
                dict[s.charAt(i)] = t.charAt(i);
            } else if (dict[s.charAt(i)] != t.charAt(i)) {
                return false;
            }
        }
        return true;
    }

四、重複元素

題目:

/**
 * 給定一個整數數組,判斷是否存在重複元素。
 * 如果任何值在數組中出現至少兩次,函數返回 true。如果數組中每個元素都不相同,則返回 false。
 *
 * 示例 1:
 * 輸入: [1,2,3,1]
 * 輸出: true
 *
 * 示例 2:
 * 輸入: [1,2,3,4]
 * 輸出: false
 *
 * 示例 3:
 * 輸入: [1,1,1,3,3,4,3,2,4,2]
 * 輸出: true
 */

這道題可以說是相當簡單了,可以直接使用我們熟知的數據結構Set來進行結題

    public static boolean method1(int[] nums) {
        HashSet<Integer> integerHashSet=new HashSet<>();
        for (int a:nums){
            integerHashSet.add(a);
        }
        if (integerHashSet.size()!=nums.length){
            return true;
        }else {
            return false;
        }
    }

但我們還是要用一些花樣來解這道題,學習嘛,使用Arrays的排序方法對數組排序,這樣相同的元素就被放到一起了,然後for循環~

    public static boolean method2(int[] nums){
        Arrays.sort(nums);
        for (int i=0;i<nums.length-1;i++){
            if (nums[i]==nums[i+1]){
                return true;
            }
        }
        return false;
    }

學過java8的同學可能有一些新思路,使用流處理,分組,計數,看看是不是和數組長度相等。使用流不關注實現只關注結果,這樣一行代碼就搞定了,但實際上他的效率並不高。

    public static boolean method3(int[] nums){
        return Arrays.stream(nums).distinct().count() != nums.length;
    }

 

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