算法_初級算法(java)

前言

初始內容:常見算法題
博客地址:芒果橙的個人博客 【http://mangocheng.com】

一、字符串

1. KMP算法

  • 概念:對字符串進行切割分組(前綴、後綴),按順序匹配時,利用分組子串提高匹配效率
  • 作用:解決字符串查找的問題
  • 時間複雜度O(m+n) 空間複雜度O(m)
  • 延伸
    • 暴力匹配算法:每次匹配失敗,都重新回溯(匹配不到,索引回到上一次匹配到的位置,再+1繼續從第一個開始匹配)

2. 替換空格

題目:將給定的字符串中的空格全部替換爲233

  • 常規方法:遍歷,查找到空格的字符串索引位置,再進行添加
  • API: string.replaceAll("\s",“233”);

3. 最長公共前綴

題目:查找給定字符串數組中的最長公共前綴

  • 特別的技巧思路:先排序
// 不使用排序則需要嵌套循環
private static void getPrefix(String[] ss) {
        String str = new String();
        A:
        for (int i = 0; i < ss[0].length() ; i++) {
            String prefix = ss[0].substring(0,i+1);
            B:
            for (int j = 1; j < ss.length  ; j++) {
                if(ss[j] .startsWith(prefix)){
                    // 全部比中
                    if(j == ss.length-1){
                        str = prefix;
                    }
                }else {
                    System.out.println(str);
                    break A;
                }
            }

        }
    }

4. 迴文串

題目:給定字符串(區分大小寫),構造出最長的迴文串(正讀、反讀一致),返回長度

private static int getLengthOfHw(String s) {
        int len = 0;
        // 1.奇數、偶數字符數量
        int oddCount = 0;
        int evenCount = 0;
        // 2.計算各個字符的特徵
        String temp = new String(s);
        while (temp.length() > 0) {
            int beforeLength = temp.length();
            System.out.println("[Param:beforeLength]" + beforeLength);
            String single = temp.charAt(0) + "";
            temp = temp.replaceAll(single, "");
            int afterLength = temp.length();
            System.out.println("[Param:afterLength]" + afterLength);
            int count = beforeLength - afterLength;
            if ((count & 1) != 1) {
                // 2.1偶數累加
                evenCount = evenCount + count;
            } else if ((count & 1) == 1) {
                // 2.2奇數保存最長的
                if (oddCount < count) {
                    oddCount = count;
                }
            }

        }
        // 3.計算總數:奇數+偶數
        len = oddCount + evenCount;
        return len;
    }

題目:驗證迴文串,忽略空格,大小寫

  • 技巧:判斷字符是否爲子母或數字 Character.isLetterOrDigit()
private static boolean vertifyHw(String s, int frontIndex, int endIndex){
        boolean flag = false;
        // 1.判斷完畢
        if (frontIndex >= endIndex) {
            flag = true;
        }
        System.out.println("[Method:vertifyHw][Params:frontIndex/endIndex]" + frontIndex + "-" + endIndex);

        // 2.逐個判斷
        char front = s.charAt(frontIndex++);
        char end = s.charAt(endIndex--);
        System.out.println("[Method:vertifyHw][Params:front/end]" + front + "-" + end);

        // 3.判斷
        if(Objects.equals(front,end)){
            flag = true;
        }

        // 4.遞歸
        if(flag){
            return vertifyHw(s,frontIndex,endIndex);
        }
        
        return flag;
    }

    private static boolean vertifyHw2(String s, int frontIndex, int endIndex) {
        // 1.判斷完畢
        if (frontIndex >= endIndex) {
            return true;
        }
        System.out.println("[Method:vertifyHw][Params:frontIndex/endIndex]" + frontIndex + "-" + endIndex);

        // 2.逐個判斷
        char front = s.charAt(frontIndex++);
        char end = s.charAt(endIndex--);
        System.out.println("[Method:vertifyHw][Params:front/end]" + front + "-" + end);

        // 3.判斷
        if (!Objects.equals(front, end)) {
            return false;
        }

        // 4.遞歸
        return vertifyHw2(s, frontIndex, endIndex);
    }

題目:給定字符串,找出其中最長的迴文子串

  • 技巧:中心擴展法
// 獲取給定字符串中,最長的迴文子串
    private static String getLongestHw(String s) {

        String longestHw = "";
        // 2.遍歷所有迴文子串
        for (int i = 1; i < s.length() - 1; i++) {
            String hw = getLongestHw(s, i);
            // 3.保存最長
            if(longestHw.length()<hw.length()){
                longestHw = hw;
            }
        }

        return longestHw;
    }

    // 根據字符串和索引,獲取以該索引爲中心的最大回文串
    private static String getLongestHw(String s, int midIndex) {

        int len = s.length();
        if (midIndex == 0 || midIndex == len) {
            return "";
        }
        // 1.左右延伸
        int endIndex = midIndex + 1;
        int startIndex = midIndex - 1;
        String hw = "";
        // 2.判斷
        while (startIndex >= 0 && endIndex <= len - 1) {
            System.out.println("[Method:even][Datas:endIndex/startIndex]" + endIndex + "/" + startIndex);
            // 2.1字符
            char start = s.charAt(startIndex);
            char end = s.charAt(endIndex);
            // 2.2迴文
            if (Objects.equals(start, end)) {
                hw = s.substring(startIndex, endIndex + 1);
                System.out.println("[Description:迴文][Data:halfEndHw]" + hw);
            } else {
            }
            ++endIndex;
            --startIndex;
        }

        return hw;
    }


// LeetCode答案
static class Solution {
        private int index, len;

        // 遍歷獲取最長迴文子串
        public String longestPalindrome(String s) {
            if (s.length() < 2)
                return s;
            for (int i = 0; i < s.length() - 1; i++) {
                PalindromeHelper(s, i, i);  // 以i爲中心,向左右兩邊延伸-奇數
                PalindromeHelper(s, i, i + 1);   // 以i,i+1兩個爲中心,向左右延伸-偶數
            }
            return s.substring(index, index + len);
        }
        // 計算起始索引和長度,得出最長迴文串: 以l、r爲中心,向左右兩邊延伸。注:若l = r,則以l爲中心,
        public void PalindromeHelper(String s, int l, int r) {
            while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) {
                l--;
                r++;
            }
            //替換最長的迴文
            if (len < r - l - 1) {
                index = l + 1;
                len = r - l - 1;
            }
        }
    }

5. 括號匹配深度

題目:給定一個合法的括號序列,獲取其深度

  • 思路:從第一個字符往後遍歷,遇到 ( 則count++,否則 count–;每次循環保存max值,max 保存每次循環中max和count的最大值
// 獲取括號的深度
    private static int getDepthOfBracket(String s) {
        // 1.記錄每輪的最大值
        int max = 0;
        int count = 0;
        // 2.遍歷所有字符
        for (int i = 0; i < s.length(); i++) {
            char a = s.charAt(i);
            if (Objects.equals(a, '(')) {
                ++count;
            } else {
                --count;
            }
            System.out.println("[Method:getDepthOfBracket-for][Datas:count/max]" + count + "/" + max);
            max = Math.max(max, count);
        }
        return max;
    }

6. 字符串轉化爲整數

題目:將字符串轉化爲整數,不合法的則返回0

  • 思路:類型轉化,將每個字符通過加法計算出來。
  • 注意點:char轉化int,是通過ascii碼。
private static int stringToInt(String s) {
        int value = 0;
        // 1.判斷是否含有正負字符

        // 2.循環計算
        for (int i = 0; i < s.length(); i++) {

            char temp = s.charAt(i);
            if (Character.isDigit(temp)) {
                // 2.1 ASCII碼的加減,減去'0' 即可獲取到與int相等的值
                int g = temp - '0';
                value = value * 10 + g;
                System.out.println("[Method:stringToInt-for][Datas:g/value]" + g + "/" + value);
            } else {
                return 0;
            }
        }
        return value;
    }

二、簡單排序算法

1.冒泡排序

它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個算法的名字由來是因爲越小的元素會經由交換慢慢“浮”到數列的頂端。【維基百科】

  • 思路:第一個字符開始,從頭到尾依次相鄰比較,大的放後面;循環此步驟
  • 空間複雜度:O(1)
  • 時間複雜度
    • 最好:O(1)
    • 最壞:O(n^2)
    • 平均:O(n^2)
// 1.冒泡排序,從小到大
public static int[] bubbleSort(int[] arr){

   for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (j + 1 == arr.length) {
                    break;
                }
                // 位移運算:一個數對另一個數位異或兩次,該數不變
                if (arr[j] > arr[j + 1]) {
                    arr[j] = arr[j] ^ arr[j + 1];
                    arr[j + 1] = arr[j] ^ arr[j + 1];
                    arr[j] = arr[j] ^ arr[j + 1];
                }
            }
   }
   return arr;
}
// 2.冒泡排序,從小到大
public static int[] bubbleSort2(int[] arr) {
		// 1.總共幾次循環
        for (int i = arr.length - 1; i > 0 ; i--) {
        	// 2.一次循環的比較
            for (int j = 0; j < i; j++) {
                // 2.1位移運算:一個數對另一個數位異或兩次,該數不變
                if (arr[j] > arr[j + 1]) {
                    arr[j] = arr[j] ^ arr[j + 1];
                    arr[j + 1] = arr[j] ^ arr[j + 1];
                    arr[j] = arr[j] ^ arr[j + 1];
                }
            }
        }
        return arr;
    }

2.選擇排序

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。【維基百科】

  • 思路:從第一個字符開始往後比較,知道找到最值,交換位置;循環此步驟
  • 空間複雜度:O(1)
  • 時間複雜度
    • 最好:O(1)
    • 最壞:O(n^2)
    • 平均:O(n^2)
// 選擇排序,從小到大
public static int[] selectionSort(int[] arr) {
        // 1.總共輪次
        for (int i = 0; i < arr.length; i++) {
            int minIndex = i;
            // 2.每輪比較次數
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[minIndex] > arr[j]) {
                    minIndex = j;
                }
            }
            // 3.交換:自己本身是最值,則不用
            if (i != minIndex) {
                arr[i] = arr[i] ^ arr[minIndex];
                arr[minIndex] = arr[i] ^ arr[minIndex];
                arr[i] = arr[i] ^ arr[minIndex];
            }
        }
        return arr;
    }

3.插入排序

工作原理是通過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。【維基百科】

  • 思路:從第一個字符開始,每次往後一個字符與前面(已經排好序)進行比較
  • 空間複雜度:O(1)
  • 時間複雜度
    • 最好:O(1)
    • 最壞:O(n^2)
    • 平均:O(n^2)
// 插入排序
public static int[] insertionSort(int[] arr){
	// 從左往右
    for (int i = 0; i < arr.length; i++) {
            // 新一個字符,逐個與前面字符的比較:從有望走
            for (int j = i - 1; j >= 0; j--) {
                if (arr[j] > arr[j+1]) {
                    arr[j] = arr[j] ^ arr[j + 1];
                    arr[j + 1] = arr[j] ^ arr[j + 1];
                    arr[j] = arr[j] ^ arr[j + 1];
                }
            }
    }
    return arr;
}


4.希爾排序

也稱遞減增量排序算法,是插入排序的一種更高效的改進版本。希爾排序是非穩定排序算法。通過將比較的全部元素分爲幾個區域來提升插入排序的性能。【維基百科】

  • 思路:先進行分組排序,再對每一組進行插入排序,每完成一次排序,都對分組數進行縮小

  • 空間複雜度:O(1)

  • 時間複雜度

    • 最好:O(1)
    • 最壞:O(n^2)
// 希爾排序
public static int[] shellSort(int[] arr) {
        // 1.獲取小組數
        int count = arr.length / 2;
        int current;
        while (count > 0) {
            for (int i = count; i < arr.length; i++) {
                current = arr[i];
                int j = i - count;
                // 分組裏的數據進行插入排序
                while (j >= 0 && current < arr[j]) {
                    arr[j + count] = arr[j];
                    j -= count;
                }
                arr[j + count] = current;
            }
            count /= 2;
        }
        return arr;
}

5.快速排序

又稱分區交換排序),簡稱快排,是對冒泡排序的一種改進。通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。【百度百科】

  • 思路:找到基準數字,每次分組(子序列)比較,遞歸排序
  • 複雜度
    • 空間複雜度:O(log2n))
    • 時間複雜度:O(nlog2n)
// 快速排序
public static int[] quickSort(int[] arr){
    quickSort(arr, 0, arr.length - 1);
    return arr;
}

// 快速排序具體方法:遞歸
public static int[] quickSort(int[] arr,int left,int right){
    	// 1.結束
        if (left > right) {
            return;
        }
        // 基準數
        int i = left;
        int j = right;
        int base = arr[left];

        // 2.比較
        while (i != j) {
            // 1.右側編號查找數字:比基準數小的索引
            while (j > i && arr[j] >= base) {
                j--;
            }
            // 2.左側編號查找數字:比基準數大的索引
            while (j > i && arr[i] <= base) {
                i++;
            }
            // 3.數字交換:
            if (j > i) {
                arr[i] = arr[i] ^ arr[j];
                arr[j] = arr[i] ^ arr[j];
                arr[i] = arr[i] ^ arr[j];
            }
        }
        // 3.基準數字和left 位置數字交換
        arr[left] = arr[i];
        arr[i] = base;

        // 4.左、右側數字再次排序
        quickSort(arr, left, i - 1);
        quickSort(arr, i + 1, right);
}

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