1. 程式人生 > >劍指offer :陣列類題目彙總

劍指offer :陣列類題目彙總

3.陣列中的重複數字:

           在一個長度為n的數組裡的所有數字都在0到n-1的範圍內。 陣列中某些數字是重複的,但不知道有幾個數字是重複的。也不知道每個數字重複幾次。請找出陣列中任意一個重複的數字。 例如,如果輸入長度為7的陣列{2,3,1,0,2,5,3},那麼對應的輸出是第一個重複的數字2。

         解題思路:一維陣列在記憶體中佔據連續的空間,可以根據下標定位對應的元素。從頭掃到尾,只要當前元素值與下標不同,就做一次判斷,numbers[i]與numbers[numbers[i]],相等就認為找到了重複元素,返回true,否則就交換兩者,繼續迴圈,交換時切忌先將numbers[numbers[i]]值進行儲存(因為numbers[i值變會影響numbers[numbers[i]])。

public class Solution {
    // Parameters:
    //    numbers:     an array of integers
    //    length:      the length of array numbers
    //    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
    //                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
    //    這裡要特別注意~返回任意重複的一個,賦值duplication[0]
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        int temp;
        if(length<=1)
            return false;
        for(int i=0 ;i<length;i++){
            while(numbers[i]!=i){
                if(numbers[i] == numbers[numbers[i]]){
                    duplication[0]=numbers[i];
                    return true;
                }else{
                   temp = numbers[numbers[i]] ;
                   numbers[numbers[i]] =numbers[i] ;
                   numbers[i] = temp;
                 }
            }
        }
    return false;
    }
}

39.陣列中出現次數超過一半的數字:

         陣列中有一個數字出現的次數超過陣列長度的一半,請找出這個數字。例如輸入一個長度為9的陣列{1,2,3,2,2,2,5,4,2}。由於數字2在陣列中出現了5次,超過陣列長度的一半,因此輸出2。如果不存在則輸出0。

        解題思路:陣列中出現次數超過陣列長度的一半,如果陣列進行了排序,那排序後位於陣列中間的數字即為所尋數字(也可能出現數組中沒有這樣的數字,注意考慮特殊情況。),陣列的排序可以利用java中的sort函式,注意匯入包。該方法改變了原有陣列。

import java.util.*;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array.length == 0)
            return 0;
        Arrays.sort(array);
        int result =  array[array.length/2] ;
        int times = 0;
        for(int i=0 ; i< array.length ; i++){
            if(result==array[i])
                times++;
        }
        if(times>array.length/2)
            return result;
        else
            return 0;
      }
}

        如果不修改陣列,在遍歷的時候儲存兩個值,一個為陣列中的數字,另一個為次數,下一個數字如果和當前數字相同,則次數加一,否則減一,次數為0 ,儲存下一個值,並開始重新計數。最後一個把次數設為1的數字即為所找數。

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array.length == 0)
            return 0;
        int result = array[0];
        int time =1;
        for(int j=0 ; j< array.length ; j++){
            if(time==0){
                result = array[j];
                time=1;
            }else if(result==array[j])
                time++;
            else
                time--;
        }
        int times = 0;
        for(int i=0 ; i< array.length ; i++){
            if(result==array[i])
                times++;
        }
        if(times>array.length/2)
            return result;
        else
            return 0;
      }
}

56.陣列中數字出現的次數:

1)陣列中只出現一次的兩個數字:

        一個整型陣列中除兩個數字之外,其餘數字都出現了兩次,請寫程式找出這兩個只出現一次的數字。要求時間複雜度為n,空間複雜度為1.

       解題思路:數字與自己的異或運算的結果為0,根據這一特性, 所有數字的異或結果為兩個只出現一次的數字的異或結果,尋找到結果中第一個為1的位數,本按照該位是否為1把陣列分成兩個陣列,每個陣列的異或結果即為兩個只出現一次的數字。

//num1,num2分別為長度為1的陣列。傳出引數
//將num1[0],num2[0]設定為返回結果
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        int resultOR = 0;
        //異或結果
        for(int i=0;i<array.length;i++)
            resultOR^=array[i];
        //尋找第一個為1的位
        int flag =1;
        int index = 0;
        while(true){
            if((resultOR & flag)!=0){
                index = flag;
                break;
            }
          flag = flag<<1;
        }
         num1[0]=0;
         num2[0]=0;
        //按照第index位是否為1分為兩個陣列
        for(int i=0;i<array.length;i++){
            if((array[i] & index)!=0)
                num1[0]^=array[i];
            else
                num2[0]^= array[i];
        }
    }
}

2)陣列中唯一隻出現一次的數字:

          在一個數組中除一個數字只出現一次之外,其他數字都出現了三次,請找出那個只出現一次的數字。

         解題思路:如果一個數字出現三次,則二進位制表示的每一位也出現3次,將所有出現三次的數字的二進位制表示的每一位都分別加起來,那麼每一位都可以被3整除。把陣列中所有數字的二進位制表示的每一位相加,如果某一位的和能被3整除,那麼只出現一次的數字的二進位制表示中對應的那一位為0,否則為1.

public class Solution {
    public void findForm3(int [] a) {
        int [] bits = new int[32];
        //將陣列中的數的二進位制表示的每一位相加
        for(int i =0;i<a.length;i++){
            for(int j =0;j<32;j++){
                bits[j] = bits[j] + ((a[i]>>>j) & 1);
            }
        }
         //結果中某一位能被3整除,則出現一次的陣列該位為0,否則為1
        int res =0;
        for(int i =0;i<32;i++){
            if(bits[i]%3!=0){
                res = res | (1<<i);
            }
        }
       return res;
    }
}

53.在排序陣列中查詢數字:

1)數字在排序陣列中出現的次數:

         統計一個數字在排序陣列中出現的次數。

         解題思路:最直接的方法,從頭到尾掃描整個陣列,統計數字出現的次數,這樣的時間複雜度為O(n)。

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
       int times=0;
       for(int i= 0;i<array.length;i++){
           if(array[i]==k)
               times++;
       }
        return times;
    }
}

       利用二分查詢法,找到數字第一次出現和最後一次出現的位置,這樣的時間複雜度為O(logn)。以尋找第一次出現位置為例,如果中間數字和k相同,則如果中間值為陣列的開始,或者中間數字的前面一個不是k,則中間值即為第一次出現k的位置;如果中間值比k大,則第一次出現位置在前半部分;如果中間值比k小,則第一次出現位置在後半段。

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
       if(array.length==0)
           return 0;
        int first = firstK(array ,0,array.length-1,k);
        int last = lastK(array ,0,array.length-1,k);
        if(first!=-1 && last!=-1)
            return (last-first+1);
        else
            return 0;
    }
    //第一次出現的位置
    public int firstK(int [] array , int start,int end, int k){
        if(start>end)
            return -1;
        int mid = (start+end)/2;
        if(array[mid]==k){
            if(( mid==0 || array[mid-1]!=k && mid>0))
                return mid;
            else 
                end = mid-1;
        }
        else if(array[mid]>k)
            end = mid-1;
        else
            start = mid+1;
        return firstK(array ,start,end,k);
    }
    //最後一次出現的位置
     public int lastK(int [] array , int start,int end, int k){
        if(start>end)
            return -1;
        int mid = (start+end)/2;
        if(array[mid]==k){
            if(mid==array.length-1 || (array[mid+1]!=k && mid<array.length-1))
                return mid;
            else 
                start = mid+1;
        }
         else if(array[mid]>k)
            end = mid-1;
        else
            start = mid+1;
        return lastK(array ,start,end,k);
    } 
}

2)0-n-1中缺失的數字:

         一個長度為n-1的遞增排序陣列中的所有數字都是唯一的,並且每個數字都在範圍0-n-1之內,在範圍0-n-1內的n個數字中有且只有一個數字不在該陣列中,請找出這個數字。

        解題思路:最直觀的解決方案,利用公式n(n-1)/2,求出所有數字之和,然後求出陣列中所有數字的和,兩者之差即為不在陣列中的數字,這樣的時間複雜度為O(n)。細究題目可以發現,該問題可以轉化為在排序陣列中找出第一個值和下標不相同的元素,對於排序陣列,可以使用二分查詢的方式。如果中間元素的值和下標不相同,並且它前面一個元素的值和下標相同,則該元素即為缺失數字,如果前一個元素的值和下標也不同,則下一次考察陣列的左半邊;如果中間元素的值和下標元素的值相同,則考察陣列的右半邊,結束條件為left<=right。

import java.util.*;
public class Solution {
    public int GetMissingNumber(int[] array){ 
        if (array == null || array.length == 0) {
            return -1; 
        } 
        int start = 0; 
        int end = array.length - 1; 
        while (start <= end) { 
            int mid = (start + end) / 2; 
            if (mid >= 0 && array[mid] != mid) { 
                if (mid == 0 || array[mid - 1] == mid - 1) {
                    return mid; 
                } else { 
                    end = mid - 1; 
                } 
            } else if (array[mid] == mid) { 
                start = mid + 1;
            }
        } 
        if (start == array.length) 
            return array.length; 
        return -1; 
    }
}

3)陣列中數值和下標相等的元素:

       假設一個單調遞增的數組裡的每個元素都是整數並且是唯一的,找出陣列中任意一個數值等於其下標的元素。

       解題思路:最直觀的解決方案,從頭到尾掃描一遍陣列,逐一檢查數值與下標是否相等,這樣時間複雜度為O(n)。對於排序陣列,可以使用二分查詢的方式。如果第i個數字的值等於i,輸出即可;當第i個數字的值大於i,那麼它右邊的數字都大於對應的下標,考慮左邊陣列即可;當第i個數字的值小於i,那麼它左邊的數字都小於對應的下標,考慮右邊陣列即可,結束條件為left<=right。

import java.util.*;
public class Solution {
     public int GetNumberSameAsIndex(int[] array){ 
         if (array == null || array.length == 0) {
             return -1; 
         } 
         int start = 0; 
         int end = array.length-1; 
         while (start <= end) {
             int mid = (start + end) / 2;
             if (array[mid] == mid) { 
                 return mid; 
             } 
             if (array[mid] > mid) { 
                 end = mid - 1;
             } 
             if (array[mid] < mid) { 
                 start = mid + 1; 
             } 
         } 
         return -1; 
     }
}

4.二維陣列中的查詢:

         在一個二維陣列中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函式,輸入這樣的一個二維陣列和一個整數,判斷陣列中是否含有該整數。

        解題思路:由於題目中的矩陣是有序的,選取陣列中右上角的數字開始進行判斷,逐步縮小查詢範圍。對於二維陣列,object [][] array ;array.length 就是行數;array [0].length 就是列數。

public class Solution {
    public boolean Find(int target, int [][] array) {
        Boolean flag = false;
        int rows = array.length;
        int colums = array[0].length;
        if( rows>0 && colums>0){
            int row =0;
            int colum = colums-1;
            while(row<rows && colum>=0){
                if(target==array[row][colum]){
                    flag = true;
                    break;
                }else if(target>array[row][colum]){
                    row++;
                }else{
                    colum--;
                }
            }
            
        }
        return flag;
    }
}

21.調整陣列順序使奇數位於偶數前面

          輸入一個整數陣列,實現一個函式來調整該陣列中數字的順序,使得所有的奇數位於陣列的前半部分,所有的偶數位於位於陣列的後半部分,並保證奇數和奇數,偶數和偶數之間的相對位置不變。

          解題思路:要想保證交換順序後奇數與偶數的相對位置不變,只能在相鄰的位置進行交換,從陣列的第一個元素開始,每一次將最後一個位置的元素固定,類似於排序演算法。或者可以新建兩個list集合,分別將奇數和偶數放到集合中,最後在進行合併。

public class Solution {
    public void reOrderArray(int [] array) {
        for(int i = 0; i<array.length-1;i++){
                for(int j = 0; j<array.length-i-1;j++){
                    if(array[j]%2==0 && array[j+1]%2!=0){
                        int temp = array[j];
                        array[j] =array[j+1];
                        array[j+1] =temp;
                    }
            }
        }
    }
}

29.順時針列印矩陣

          輸入一個矩陣,按照從外向裡以順時針的順序依次打印出每一個數字,例如,如果輸入如下矩陣: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 則依次打印出數字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

       解題思路:矩陣順時針列印,需要考慮終止條件以及最後一圈的情況,可能只有一行,或只有一列或者只有一個數字,在列印時需考慮上下左右的情況,設定列印條件(尤其是最後的向上列印)

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printMatrix(int [][] matrix) {
        ArrayList<Integer> result = new ArrayList<Integer>();
        int rows = matrix.length;
        int colums = matrix[0].length;
        if(rows==0 && colums==0)
            return null;
        int left = 0, right = colums-1,up = 0,down = rows-1;
        while(left<=right && up<=down){
            for(int i=left;i<=right;i++)
                result.add(matrix[up][i]);
            if(down>up){//第二步
                for(int j=up+1;j<=down;j++)
                    result.add(matrix[j][right]);
                if(right>left){//第三步
                    for(int k=right-1;k>=left;k--)
                        result.add(matrix[down][k]);
                     if(down-1>up){//第四步
                         for(int t=down-1;t>up;t--)
                            result.add(matrix[t][left]);
                }}}
            left++; right--; up++; down--;
        }
        return result;
    }
}

11.旋轉陣列的最小數字:

           把一個數組最開始的若干個元素搬到陣列的末尾,我們稱之為陣列的旋轉。 輸入一個非遞減排序的陣列的一個旋轉,輸出旋轉陣列的最小元素。 例如陣列{3,4,5,1,2}為{1,2,3,4,5}的一個旋轉,該陣列的最小值為1。 NOTE:給出的所有元素都大於0,若陣列大小為0,請返回0。

         解題思路:利用二分查詢的思想,當兩個輔助變數相遇時,結束迴圈,第二個變數所指即為最小的元素,注意考慮特殊情況,(1)當旋轉陣列和本身陣列相同時,第一個數字即為最小的元素,因此mid初始化設定為0;(2)如果陣列中有相同的數字,start、end、mid下標所指的值恰巧相等,此時需要順序查詢的方法來尋找最小的值。

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array.length==0)
            return 0;
        int start = 0;
        int end = array.length-1;
        int mid = start;
        while(array[start]>=array[end]){
            if(end-start ==1)
                return array[end];
            mid = (end+start)/2;
            //如果陣列中有相同的數字,例如{1,1,1,0,1}
            if(array[mid]==array[end] && array[mid]==array[start]){
                int result = array[0];
                for(int i=0;i<array.length;i++){
                    if(result>array[i])
                        result = array[i];
                }
                return result;
            }
            if(array[mid]>=array[end])
                start = mid;
            else if(array[mid]<=array[start])
                end = mid;
        }
        return array[mid];
    }
}

42.連續子陣列的最大和:

          輸入一個整型陣列,數組裡有整數也有負數,陣列中的一個或者連續多個整陣列成一個子陣列,求所有子陣列的和的最大值,要求時間複雜度為n。

         解題思路:設定結果為陣列的第一個值,依次掃描陣列進行累加,當陣列中的元素值大於累加結果,重新進行累加;當累計結果比要輸出的結果大時,更新輸出結果。


public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        if(array.length==0)
            return 0;
        else{
            int total=array[0],maxSum=array[0];
            for(int i=1;i<array.length;i++){
                if(total>=0)
                    total+=array[i];
                else
                    total=array[i];
                if(total>maxSum)
                    maxSum=total;
            }
            return maxSum;
        }    
    }
 }

51.陣列中的逆序對:

          在陣列中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個陣列中的逆序對的總數P。並將P對1000000007取模的結果輸出。 即輸出P%1000000007

         解題思路:採用歸併排序的方式,時間複雜度為N(nlogn),先把陣列分隔成子陣列,統計出子陣列內部的逆序對數目,然後再統計出兩個相鄰子陣列之間的逆序對數目。用兩個輔助變數指向陣列的末尾,每次比較其數字大小,如果左陣列數字大於右陣列數字,逆序對的數目=右陣列中剩餘數字的個數。

public class Solution {
    public int InversePairsCore(int [] array,int [] copy,int start,int end){
        if(start==end)
            return 0;
        int mid = (start+end)/2;
        int left = InversePairsCore(array,copy,start,mid)%1000000007;
        int right = InversePairsCore(array,copy,mid+1,end)%1000000007;
        int i = mid,j = end;//前半段末尾,後半段末尾
        int index = end;//copy陣列末尾
        int count=0;
        while(i>=start && j>=mid+1){
            if(array[i]>array[j]){
                copy[index--]=array[i--];
                count+=j-mid;
                if(count>=1000000007)//數值過大求餘                
                    count%=1000000007;
            }else
                copy[index--]=array[j--];
        }
        for(;j>=mid+1;j--)
            copy[index--]=array[j];
        for(;i>=start;i--)
            copy[index--]=array[i];
         for(int s=start;s<=end;s++)
            array[s] = copy[s];
        return (count+left+right)%1000000007;
    }
    public int InversePairs(int [] array) {
        if(array ==null || array.length ==0)
            return 0;
        int [] copy = new int[array.length];
        for(int i = 0; i<array.length;i++)
            copy[i]=array[i];
        int count = InversePairsCore(array,copy,0,array.length-1);
        return count;
    }
}


66.構建乘積陣列:

           給定一個數組A[0,1,...,n-1],請構建一個數組B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。

           解題思路:如果可以用除法,B的每一位即為所有數的乘積除以A中所對應的那一位的值;不可以用除法,最直觀的解法是用連乘n-1個數來得到每一位的結果,這樣的時間複雜度為O(n^2),更簡單的方法對於求第i位的結果,可以分為兩部分,先求出前i-1位的結果,然後再乘以第i位後的乘積。後面的乘積可以構建輔助變數,指向陣列的最後一位,依次向前乘。

import java.util.ArrayList;
public class Solution {
    public int[] multiply(int[] A) {
        int [] B = new int[A.length];
        if(A.length ==0 || A ==null)
            return B;
        if(A.length ==1)
            return A;
        B[0]=1;
        for(int i=1;i<A.length;i++)
           B[i] = B[i-1]*A[i-1];
        int temp =1;
        for(int i = A.length-2 ;i>=0;i--){
            temp *= A[i+1];
            B[i] = B[i]*temp;
        }
        return B;
    }
}

61.撲克牌順子:

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

       解題思路:把撲克牌抽象成陣列,首先將陣列排序,其次統計陣列中0的個數,最後統計排序之後的陣列中相鄰數字之前的空缺總數,如果空缺總數小於或者等於0的個數,那麼,這個陣列就是連續的,否則不連續。注意,如果5張牌中存在相等的兩個數,則不可能成為順子。

import java.util.*;
public class Solution {
    public boolean isContinuous(int [] numbers) {
        boolean result = false;
        if(numbers==null || numbers.length<=0)
            return result;
        Arrays.sort(numbers);
        int numOfZero = 0;
        for(int i =0;i<numbers.length;i++){
            if(numbers[i]==0)
                numOfZero++;
        }
        int min = numOfZero;
        int max = min+1;
        int dif =0;
        while(max<numbers.length){
            if(numbers[min]==numbers[max])
                return result;//存在對子
            dif += numbers[max]-numbers[min]-1;//差距
            min = max;
            max++;
        }
        if(dif<=numOfZero)
            result = true;
        return result;           
    }
}