資料結構及演算法

一隻有交流障礙的醜程式猿發表於2018-04-13

本文是Android面試題整理中的一篇,結合右下角目錄食用更佳,包括:

  • 資料結構
  • 排序演算法
  • 加解密
  • 常見題型舉例

資料結構


1. 二叉樹

資料結構及演算法

每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”(left subtree)和“右子樹”(right subtree)。二叉樹常被用於實現二叉查詢樹和二叉堆。

2. 完全二叉樹

若設二叉樹的高度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第h層有葉子結點,並且葉子結點都是從左到右依次排布,這就是完全二叉樹。
特性:完全二叉樹中任何一層最左的節點編號n,則其左子樹為2n,右子樹為2n+1,利用這種特性,可以用陣列作為二叉樹的物理結構

3. 滿二叉樹

除了葉結點外每一個結點都有左右子葉且葉子結點都處在最底層的二叉樹。
特點:滿二叉樹有2^k-1個節點(k為高度)

4. 平衡二叉樹

它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹

5. 紅黑樹

是一種自平衡二叉查詢樹
特性:

  1. 節點是紅色或黑色。
  2. 根節點是黑色。
  3. 每個葉節點(NIL節點,空節點)是黑色的。
  4. 每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點)
  5. 從任一節點到其每個葉子的所有路徑都包含相同數目的黑色節點。

6. 二叉樹的遍歷

    // 節點
    class Node{
        int value;
        Node leftNode;
        Node rightNode;
    }


    //以下知識簡略步驟,省去判空等操作,每種只選取了一種簡單實現方式
    // 遍歷的順序指訪問根結點的操作發生在遍歷其左右子樹的前後中
    // 前序 (遞迴)
    public void preOrder(Node node){
        System.out.print(node.value);
        preOrder(node.leftNode);
        preOrder(node.rightNode);
    }

    // 中序 (遞迴)
    public void inOrder(Node node){
        inOrder(node.leftNode);
        System.out.print(node.value);
        inOrder(node.rightNode);
    }

    // 後序 (遞迴)
    public void posOrder(Node node){
        posOrder(node.leftNode);
        posOrder(node.rightNode);
        System.out.print(node.value);
    }

    // 計算深度 (遞迴)
    public int level(Node node){
        if (node == null)
            return 0;
        return level(node.leftNode) + 1 > level(node.rightNode) + 1 ? level(node.leftNode) + 1
                : level(node.rightNode) + 1;
    }

    // 層序 (非遞迴)
    public void levelOrder(Node node){

        Queue<Node> nodes = new LinkedList<>();

        nodes.add(node);

        while (!nodes.isEmpty()){
            Node node1 = nodes.poll();
            System.out.print(node1.value);
            nodes.offer(node1.leftNode);
            nodes.offer(node1.rightNode);
        }
    }
複製程式碼

7. 堆的概念

堆是一棵完全二叉樹,分為大根堆(父結點>子節點)和小根堆(父結點<子節點)

演算法


1. 排序

   //氣泡排序(時間複雜度n^2)
    public int[] blueblueSort(int[] array){
        for (int i = 0 ; i < array.length ; i++){
            for (int j = 0 ; j< array.length -i-1; j++){
                if (array[j] > array[j+1]){
                    int temp = array[j+1];        // 元素交換
                    array[j+1] = array[j];
                    array[j] = temp;
                }
            }

        }
        return array;
    }


     //選擇排序 (n^2)
     public int[] sellectionSort(int[] array){
        for (int i = 0 ; i < array.length - 1 ; i++){
            int tempk = i;
            for (int j = i+1 ; j < array.length; j++){
               if (array[tempk] > array[j]){
                   tempk = j;
               }
            }

            int temp = array[i];
            array[i] = array[tempk];
            array[tempk] = temp;

        }
        return array;
     }

     //插入排序 (n^2)
    public int[] insertSort(int[] array){
        for (int i = 1 ; i < array.length; i++){

            int j = i-1;
            int current = array[i];
            while (j >= 0 && array[j] > current ){
                array[j+1] = array[j];
                j--;
            }
            array[j+1] = current;

        }

         return array;
    }


   // 歸併排序 (n log n)
    public int[] mergeSort(int[] array){
        int length = array.length;
        if (length < 2){
            return array;
        }

        int[] left = new int[array.length/2];
        int[] right = new int[array.length - array.length/2];
        for (int i = 0; i < array.length/2 ; i++){
            left[i] = array[i];
        }
        for (int j = 0;j < right.length;j++){
            right[j] = array[array.length/2+j];
        }

        return merge(mergeSort(left),mergeSort(right));
    }

    public int[] merge(int[] left, int[] right){
        int[] result = new int[left.length+right.length];
        int i = 0;
        int j = 0;
        int k = 0;
        while (i < left.length && j < right.length){
            if (left[i] > right[j]){
                result[k] = right[j];
                j++;
            } else {
                result[k] = left[i];
                i++;
            }
            k++;
        }

        while (i<left.length){
            result[k] = left[i];
            k++;
            i++;
        }

        while (j<right.length){
            result[k] = right[j];
            k++;
            j++;
        }

        return result;
    }
    
    // 快排 (n log n)
      public void quickSort1(int[] array,int begin ,int end) {
        if (begin<end){
            int p = partition(array,begin,end);
            quickSort1(array,begin,p-1);
            quickSort1(array,p+1,end);
        }

    }

    public int partition(int[] array,int begin ,int end){
        int key = array[begin];
        int left = begin;
        int right = end;
        while (left < right){

            while (array[right] > key && left < right)
                right--;


            while (array[left] <= key && left < right)
                left++;


            if (left < right){
                int temp1 = array[left];
                array[left] = array[right];
                array[right] = temp1;
            }



        }
        array[begin] = array[left];
        array[left] = key;
        return left;
    }

複製程式碼

2, 二分查詢

時間複雜度log n

//非遞迴
public static int binarySearch(Integer[] srcArray, int des) {
    //定義初始最小、最大索引
    int low = 0;
    int high = srcArray.length - 1;
    //確保不會出現重複查詢,越界
    while (low <= high) {
        //計算出中間索引值
        int middle = (high + low)>>>1 ;//防止溢位
        if (des == srcArray[middle]) {
            return middle;
        //判斷下限
        } else if (des < srcArray[middle]) {
            high = middle - 1;
        //判斷上限
        } else {
            low = middle + 1;
        }
    }
    //若沒有,則返回-1
    return -1;
}

//遞迴

    public int binarySearch(int[] array,int begin,int end,int key){

        if ( begin > end ){
            return -1;
        }


        int mid = begin + end >>> 1;
        if (array[mid] == key){
            return mid;
        } else if (array[mid] > key){
            return binarySearch(array,begin,mid-1,key);
        } else {
            return binarySearch(array,mid+1,end,key);
        }

    }

複製程式碼

3. 計數排序

如果在面試中有面試官要求你寫一個O(n)時間複雜度的排序演算法,你千萬不要立刻說:這不可能!雖然前面基於比較的排序的下限是O(nlogn)。但是確實也有線性時間複雜度的排序,只不過有前提條件,就是待排序的數要滿足一定的範圍的整數,而且計數排序需要比較多的輔助空間。其基本思想是,用待排序的數作為計數陣列的下標,統計每個數字的個數。然後依次輸出即可得到有序序列。

4. 堆排序

public void heapSort(int[] array){
        for (int i = array.length / 2 ; i >= 0 ; i--){ //初始化堆
            heapAdjust(array,i,array.length-1);
        }

        for (int i = array.length-1;i>0;i--){ //排序
            int temp = array[i];
            array[i] = array[0];
            array[0] = temp;

            heapAdjust(array,0,i);
        }

  }



  private void heapAdjust(int[] array , int parent, int length){

      int temp = array[parent];

      int child = parent*2+1;

      while (child < length){
          if (child+1<length && array[child]<array[child+1] )
              child++;

          if (temp > array[child])
              break;

          array[parent] = array[child];

          parent = child;
          child = 2*child+1;
      }

      array[parent] = temp;


  }
複製程式碼

5. 快排範型實現

 public static <T extends Comparable<T>> void quickSort(T[] array, int left, int right) {
        if (left < right){
            int p = partition(array,left,right);
            quickSort(array,left,p-1);
            quickSort(array,p+1,right);
        }


    }

    private static <T extends Comparable<T>> int partition(T[] array, int left, int right) {
        T key = array[left];
        int start = left;

        while (left<right){

            while (left < right && array[right].compareTo(key) > 0)
                right --;

            while (left < right && array[left].compareTo(key) <= 0)
                left ++;

            if (left < right){
                T temp1 = array[left];
                array[left] = array[right];
                array[right] = temp1;
            }
        }

        array[start] = array[left];
        array[left] = key;
        return left;
    }
複製程式碼

6. 氣泡排序範型

    public static  <T extends Comparable<T>> void bubbleSort(T[] array){

        for (int i = 0  ; i < array.length - 1 ; i++){ //外層迴圈控制排序趟數

            for (int j = 0 ; j < array.length - i- 1 ; j++){
                if (array[j].compareTo(array[j+1])>0){ //內層迴圈控制每一趟排序多少次
                    T temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                }
            }
        }

    }
複製程式碼
public class CountSort {

public static void countSort(int[] arr) {
    if(arr == null || arr.length == 0)
        return ;

    int max = max(arr);

    int[] count = new int[max+1];
    Arrays.fill(count, 0);

    for(int i=0; i&lt;arr.length; i++) {
        count[arr[i]] ++;
    }

    int k = 0;
    for(int i=0; i&lt;=max; i++) {
        for(int j=0; j&lt;count[i]; j++) {
            arr[k++] = i;
        }
    }

}

public static int max(int[] arr) {
    int max = Integer.MIN_VALUE;
    for(int ele : arr) {
        if(ele &gt; max)
            max = ele;
    }

    return max;
}

}
複製程式碼

加解密


0. Base64

Base64 是一種編碼方式,不具有可讀性,便與傳輸

1. MD5

  1. 壓縮性:任意長度的資料,算出的MD5值長度都是固定的。
  2. 容易計算:從原資料計算出MD5值很容易。
  3. 抗修改性:對原資料進行任何改動,哪怕只修改1個位元組,所得到的MD5值都有很大區別。
  4. 強抗碰撞:已知原資料和其MD5值,想找到一個具有相同MD5值的資料(即偽造資料)是非常困難的。
  5. 不可逆:知道md5,不能還原出加密前資料

2. [MD5加鹽](https://blog.csdn.net/blade2001/article/details/6341078)

3. AES 對稱加密

4. RSA 非對稱加密

例題


1. 找陣列中第k大的數字

使用類似快排的方式:

  1. 隨機選一個基值X,分為將陣列分為兩塊Sa > X > Sb
  2. Sa個數大於k,則遞迴Sa找第k大數;Sa個數小於k,遞迴Sb找第(k-Sa.size)大數
    複雜度O(n)

2. 在兩個等長有序陣列中找到中位數

  1. X[n/2],Y[n/2];比較兩數的大小,若X[n/2]>Y[n/2],那麼我們可以捨去X[n/2]之後和Y[n/2]之前的數;若X[n/2]<Y[n/2],對應的,我們可以捨去X[n/2]之前和Y[n/2]之後的數;若X[n/2]=Y[n/2],那表明當前值就是所求的中位數。
  2. 遞迴的在剩餘的數中進行比較篩選

3. 判斷連結串列中是否存在環

兩個指標一快一慢,有環的話快慢肯定會相遇

4. 判斷一個二叉樹是不是平衡二叉樹

平衡二叉樹的定義:空樹或者左右子樹高度相差不超過1;子樹也是平衡二叉樹
利用這一特性,我們可以用遞迴的方式判斷

class TreeNode{
    int val;
    TreeNode left=null; 
    TreeNode right=null;
    public TreeNode(int val) {
        this.val = val;
    }
}

public boolean IsBalanced_Solution(TreeNode root) {         
 if(root==null)
    return true;
//如果樹為 null 返回 TRUE。否則判斷根的左右子樹的高度差的絕對值是否大於1,若大於1 則返回false。
// 否則判斷樹的左右孩子是否是平衡二叉樹,當兩者都是平衡二叉樹時返回TRUE,否則返回false.
  else if(Math.abs(TreeDepth(root.left)-TreeDepth(root.right))>1)
         return false;
  else return IsBalanced_Solution(root.left)&&IsBalanced_Solution(root.right);

 }
 
//求樹的深度。
public int TreeDepth(TreeNode root){
  if(root==null)
   return 0;
  //如果樹為 null 返回0 否則返回左右孩子的最大值+1。
  return Math.max(TreeDepth(root.left), TreeDepth(root.right))+1;
}


因為上述方法會多次計算相同子樹深度,優化:
public class IsBalancedTree {
    boolean isBalance=true;
    public boolean IsBalanced_Solution(TreeNode root) {         
         TreeDepth1(root);
         return isBalance;
        //isBalance 會在 TreeDepth1(root)中賦值。
        }
    public int TreeDepth1(TreeNode root)
     {
         if(root==null)
             return 0;
         int left=TreeDepth1(root.left);
         //左子樹高度
         int right=TreeDepth1(root.right);
         //右子樹高度
         if(Math.abs(left-right)>1)
         {
             isBalance=false;
             //只要有一個子樹的左右子樹的高度絕對值大於 1 isBalance=false
         }
         return Math.max(left, right)+1;
     }
     
複製程式碼

5. 從撲克牌中隨機抽 5 張牌,判斷是不是順子,即這 5 張牌是不是連續的。 2-10 為數字本身,A 為 1,J 為 11,Q 為 12,K 為 13,而大小王可以看成任意的 數字。

解題思路:我們可以把5張牌看成是由5個數字組成的俄陣列。大小王是特殊的數字,我們可以把它們都定義為0,這樣就可以和其他的牌區分開來。
首先把陣列排序,再統計陣列中0的個數,最後統計排序之後的陣列中相鄰數字之間的空缺總數。如果空缺的總數小於或者等於0的個數,那麼這個陣列就是連續的,反之則不連續。如果陣列中的非0數字重複出現,則該陣列是不連續的。換成撲克牌的描述方式就是如果一幅牌裡含有對子,則不可能是順子。

public boolean isContinuous(int[] array){
     Arrays.sort(array);
     int wang = 0;
     int gap = 0;
        
     for (int i : array){
      if (array[i] == 0){
         wang++;
         continue;
       }
      if (i < array.length-1){
         if (array[i] == array[i+1]){
             return false;
         } else {
           gap += array[i+1] - array[i]-1;
       }
      }
    }
    return wang >= gap;
    }
複製程式碼

6. 圓圈中最後剩下的數字

題目:0,1,...,n-1這n個數字排成一個圓圈,從數字0開始每次從這個圓圈裡刪除第m個數字。求這個圓圈裡剩下的最後一個數字。

變形:標號1-n的n個人首尾相接,1到3報數,報到3的退出,求最後一個人的標號

解答:
1、環形連結串列模擬圓圈

建立一個n個節點的環形連結串列,然後每次在這個連結串列中刪除第m個節點;

可以用std::list來模擬環形連結串列,list本身不是環形結構,因此每當迭代器掃描到連結串列末尾的時候,需要將迭代器移到連結串列的頭部。

2、分析每次被刪除的數字的規律,動態規劃

假設從0-n-1中刪除了第m個數字,則下一輪的數字排列為m,m+1,.....n,1,2,3...m-2,將該數字排列重新對映為0~n-2,則為

m&emsp;&emsp;&emsp;&emsp;0

m+1&emsp;&emsp;  1&emsp;&emsp;

....&emsp;&emsp;&emsp; ....

n-1&emsp;&emsp;&emsp;n-1-m

0&emsp;&emsp;&emsp;&emsp;n-m

1&emsp;&emsp;&emsp;&emsp;n-m+1

...&emsp;&emsp;&emsp;&emsp;....

m-2&emsp;&emsp;  n-2

可以看出從右往左的對映關係為left=(right+m)%n,即0~n-1序列中最後剩下的數字等於(0~n-2序列中最後剩下的數字+m)%n,很明顯當n=1時,只有一個數,那麼剩下的數字就是0.

問題轉化為動態規劃問題,關係表示為:

f(n)=(f(n-1)+m)%n; 當n=1,f(1)=0;

程式碼如下:

public static int lastRemaining(int n, int m){
		if(n < 1 || m < 1){
			return -1;
		}
		int last = 0;
		for(int i = 2; i <= n; i++){
			last = (last + m) % i;
		}
		return last;
	}
複製程式碼

7. 給定一個字串,求第一個不重複的字元 abbcad -> c

思路:和計數排序類似,通過一個陣列來表示字元(陣列太長時也可以考慮bitmap),統計每個位置的個數。

8. 重建二叉樹

題目:輸入二叉樹的前序遍歷和中序遍歷的結果,重建出該二叉樹。假設前序遍歷和中序遍歷結果中都不包含重複的數字,例如輸入的前序遍歷序列 {1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6}重建出如圖所示的二叉樹。

前序遍歷第一個地址是父地址,在中序遍歷中,此地址前是左子樹(個目m),右邊是右子樹(個數n);前序遍歷第一個地址後的m個是其左子樹,其餘是其右子樹。有了左右子樹的前序遍歷和中序遍歷,我們就可以用遞迴的方法來構建樹了

public class BinaryTreeNode {

	public static int value;
	public BinaryTreeNode leftNode;
	public BinaryTreeNode rightNode;
}

public BinaryTreeNode build(int[] pre, int[] in){
if(pre == null || in == null || pre.length != in.length){
  throw new Exception("heiheihei");
}

BinaryTreeNode root = new BinaryTreeNode();
root.value = pre[0];
for(int i = 0; i < in.length ;i++){
   if(pre[0] == in[i]){
       root.leftNode = build(Array.copyOfRange(pre,1,i+1),Array.copyOfRange(in,0,i)); 
       root.rightNode = build(Array.copyOfRange(pre,i+1,pre.length),Array.copyOfRange(in,i+1,in.length)); 
   break;
   }

}

return root;

}

複製程式碼

9. two-sum

給定一個陣列和一個目標值target,在陣列中找到一組和為target的數,並輸出他們的下標,並且要求下標1 < 下標2

public int[] find(int[] array,int target){
HashMap<int,int> map = new HashMap<>();
int[] result = new int[2];
for(int i = 0;i<array.length;array++){
    if(map.get(array[i])!=null){
     result[0] = map.get(array[i])+1; 
     result[1] = i+1;
     break;
    }
}
return result;
}

複製程式碼

10. 實現一個特殊的棧,在實現棧的基本功能的基礎上,在實現返回棧中最小元素的操作(有求存、取、getmin時間複雜度都為1)

思路:通過兩個棧來實現,一個棧正常儲存,另一個棧記錄最小資料

11. 編寫一個類,用兩個棧實現佇列,支援佇列的基本操作(add、poll、peek)

思路:將棧1資料存入棧2,即完成了讀取棧2的內容,即是佇列。
解法:1.add時將資料存入棧1 2.讀取時讀取棧2,若棧2為空,將棧1資料存入棧2

12. 如何僅用遞迴函式和棧操作逆序一個棧

13. 單連結串列反轉java程式碼

class Node{
int value;
Node next;
}

// 非遞迴
public Node reverse(Node node){
Node pre = null;
Node now = node;
while(now!=null){
Node temp = now.next;
now.next = pre;
pre = now;
now = temp;
}
return pre;
}


//遞迴
public Node reverse3(Node node) {
    if(node.next==null)return node;
    Node next = node.next;
    node.next = null;
    Node re = reverse3(next);
    next.next = node;
    return re;
  }
複製程式碼

14, 有一個一維整型陣列int[]data儲存的是一張寬為width,高為height的圖片畫素值資訊。請寫一個演算法,將該圖片所有的白色不透明(0xffffffff)畫素點的透明度調整為50%。

final int size = data.length;

for(int i = 0; i< size; i++){

     if(data[i] == 0xffffffff)

            data[i] = 0x80ffffff;

}

複製程式碼

15. int a = 10; int b=5; 怎麼在不引入其他變數的情況下,讓a和b互換?

//方法1
a = a+b;
b = a-b;
a = a-b; 

//方法2
a = a^b;
b = b^a;
a = a^b;
複製程式碼

16. 在一個長字串A中找一個短字串

KMP演算法

17. 求二進位制數中1的個數

任意給定一個32位無符號整數n,求n的二進位制表示中1的個數,比如n = 5(0101)時,返回2,n = 15(1111)時,返回4

思路:利用n&(n-1)消除n轉換成二進位制後最低位的1

public void count(int n){
int count = 0;
while(n>0){
count++;
n = n&(n-1)
}

複製程式碼

擴充套件:求一個數是不是偶數:n > 0 && ((n & (n - 1)) == 0 )

18. 將陣列中元素排列為奇數在前偶數在後

思路:和快排思想類似,從前向後找到偶數,從後向前找到奇數,交換

    private void jiOu(int a[])  //將陣列a中奇數放在前面,偶數放在後面  
        {  
            int len = a.length;  
            if(len <= 0) //陣列長度為0則返回  
                return ;  
            int front = 0, end = len-1;//設定兩個指標,一個指向頭部,一個指向尾部  
            while(front<end)  
            {  
                while(front<len && (a[front]&1)==1)  //從前往後找偶數  
                    front++;  
                while(end>=0 && (a[end]&1)==0)   //從後往前找奇數  
                    end--;  
                if(front<end)  
                {  
                    int swap = a[front];    //將奇數往前挪,偶數往後挪  
                    a[front] = a[end];  
                    a[end] = swap;  
                }  
            }  
        }  
複製程式碼

19. 找出未打卡的員工

輸入兩行資料,第一行為全部員工的 id,第二行為某一天打卡的員工 id,已知只有一個員工沒有打卡,求出未打卡員工的 id。(員工 id 不重複,每行輸入的 id 未排序)

  1. 思路1:遍歷一邊員工id,將員工id填入HashMap或計數排序;再遍歷一遍打卡員工,從HashMap或者技術排序的陣列中查詢。因為HashMap和技術排序的陣列查詢的時間複雜度都是1,隨意整體的時間複雜度是兩次遍歷即2n
  2. 思路2:我們有兩個陣列,打卡的員工id肯定會在兩個陣列裡各出現一次,一共2次,未打卡的員工id只出現了一次;利用a^a = 0;0^b = b;的特性。
       int result = 0;
        for (int i = 0; i < ids.length; i++) {
            result ^= Integer.parseInt(ids[i]);
        }
        for (int i = 0; i < marks.length; i++) {
            result ^= Integer.parseInt(marks[i]);
        }
複製程式碼

20. 賽馬

25匹馬,速度都不同,但每匹馬的速度都是定值。現在只有5條賽道,無法計時,即每賽一場最多隻能知道5匹馬的相對快慢。問最少賽幾場可以找出25匹馬中速度最快的前3名?

  1. 25匹馬分成5組,先進行5場比賽
  2. 再將剛才5場的冠軍進行第6場比賽,得到第一名。按照第6場比賽的名詞把前面5場比賽所在的組命名為 A、B、C、D、E 組,即 A 組的冠軍是第6場第一名,B 組的冠軍是第二名 …
  3. 分析第2名和第3名的可能性,如果確定有多於3匹馬比某匹馬快,那它可以被淘汰了。因為 D 組是第6場的第四名,整個D 組被淘汰了,同意整個 E 組被淘汰。剩下可能是整體的第2、3名的就是C組的第1名、B組的1、2名、A組的第2、3名。取這5匹馬進行第7場比賽
  4. 所以,一共需要7場比賽

21. 撲克牌隨機發牌

對於52張牌,實現一個隨機打算撲克牌順序的程式。52張牌使用 int 陣列模擬

思路:隨機選取一張,和第一張交換;再剩下的牌中選取一張,和第二張交換,以此類推

public void randomCards() {
    int[] data = new int[52];
    Random random= new Random();
    for (int i = 0; i < data.length; i++)
        data[i] = i;

    for (int i = data.length - 1; i > 0; i--) {
        int temp = random.nextInt(i+1); //產生 [0,i] 之間的隨機數
        swap(data,i,temp);
    }
}
複製程式碼

22. 括號字串是否合法

某個字串只包括 ( 和 ) ,判斷其中的括號是否匹配正確,比如 (()()) 正確, ((())() 錯誤, 不允許使用棧 。

思路:這種類似題的常見思路是棧,對於左括號入棧,如果遇到右括號,判斷此時棧頂是不是左括號,是則將其出棧,不是則該括號序列不合法;面試官要求不能使用棧,可以使用計數器,利用 int count 欄位。

public static boolean checkBrackets(String str) {
    char[] cs = str.toCharArray();
    int count = 0;
    for (int i = 0; i < cs.length; i++) {
        if (cs[i] == '(')
            count++;
        else {
            count--;
            if (count < 0) {
                return false;
            }
        }
    }

    return count == 0;
}
複製程式碼

23. 字串是否包含問題

假設這有一個各種字母組成的字串A,和另外一個字串B,字串裡B的字母數相對少一些。什麼方法能最快的查出所有小字串B裡的字母在大字串A裡都有?

思路1:使用計數排序,複雜度m+n 思路2: 藉助hashmap

24. 查詢兄弟單詞

一個單詞單詞字母交換,可得另一個單詞,如army->mary,成為兄弟單詞。提供一個單詞,在字典中找到它的兄弟。描述資料結構和查詢過程

思路:使用HashMap和連結串列,單詞排序後的值作為HashMap的key,連結串列作為value

25. 連結串列環路問題

一個url指向的頁面裡面有另一個url,最終有一個url指向之前出現過的url或空,這兩種情形都定義為null。這樣構成一個單連結串列。給兩條這樣單連結串列,判斷裡面是否存在同樣的url。url以億級計,資源不足以hash

思路:先判斷是否有環路(快慢指標),再判斷是否交叉(有交叉最後指向相同節點)

26. 尋找最長不重複子串

  1. 遍歷字串,過程中將出現過的字元存入字典,key為字元,value為字元下標
  2. 用maxLength儲存遍歷過程中找到的最大不重複子串的長度
  3. 用start儲存最長子串的開始下標
  4. 如果字元已經出現在字典中,更新start的值
  5. 如果字元不在字典中,更新maxLength的值
  6. return maxLength

27. Leetcode 買賣股票問題

參考資料

https://blog.csdn.net/fengqiangfeng/article/details/8049903

相關文章