《劍指offer》

1.字符串的排列

1.1.題目

題目描述

輸入一個字符串,按字典序打印出該字符串中字符的所有排列。例如輸入字符串a(chǎn)bc,則打印出由字符a,b,c所能排列出來的所有字符串a(chǎn)bc,acb,bac,bca,cab和cba。

輸入描述

輸入一個字符串,長度不超過9(可能有字符重復(fù)),字符只包括大小寫字母。

1.2.解法一

思路:根據(jù)當(dāng)前字符串Sn,計(jì)算出比它大1的字符串Sn+1,然后將Sn+1編程當(dāng)前狀態(tài),遞歸執(zhí)行。

class Solution {
public:
    void sort_str(string &str) {
        for(int i=1 ; i<str.size(); ++i)
            for(int j=i ; j>0 && str[j]<str[j-1] ; --j)
                swap(str[j], str[j-1]);
    }
    
    //必須保證rv非空
    void f(vector<string> &rv){
        string s=rv[rv.size()-1];
        int i;
        for(i=s.size()-1 ; i>0 && s[i]<=s[i-1] ;--i)
            ;
        if(i==0) return;
        int j;
        for(j=s.size()-1 ; s[j]<=s[i-1]; --j)
            ;
        swap( s[i-1], s[j] );
        int n=s.size()-1;
        while(n>i)
            swap(s[i++],s[n--]);//可別把這步給忘了呀!要嚴(yán)謹(jǐn)!嚴(yán)謹(jǐn)?。?!
        rv.push_back(s);
        f(rv);
    }
    
    vector<string> Permutation(string str) {
        vector<string> rv;
        if(str.empty()) return rv;
        sort_str(str);
        rv.push_back(str);
        f(rv);
        return rv;
    }

1.3.解法二(參考了《劍指offer》)

注意,我看的是紀(jì)念版的《劍指offer》,這個版本作者給出的代碼是錯的。應(yīng)該要傳值,而不是指針。

class Solution {
public:
    vector<string> Permutation(string str) {
        vector<string> rv;
        dfs(rv, str, 0);
        return rv;
    }
    void dfs(vector<string> &rv, string s, size_t start){
        if(start==s.size()) return;
        size_t starth=start;
        while(starth<s.size()-1 && s[starth]==s[starth+1])
            ++starth;
        if(starth==s.size()-1){
            rv.push_back(s);
            return;
        }
        for(int i=start; i<s.size();++i){
            char temp=s[i];
            swap(s[start],s[i]);
            dfs(rv,s,start+1);
            while(i<s.size()-1 && temp==s[i+1])//考慮到有重復(fù)的字符,所以要有這個循環(huán)
                ++i;
        }
    }
};

2.數(shù)組中出現(xiàn)次數(shù)超過一半的數(shù)字

2.1.題目

題目描述

數(shù)組中有一個數(shù)字出現(xiàn)的次數(shù)超過數(shù)組長度的一半,請找出這個數(shù)字。例如輸入一個長度為9的數(shù)組{1,2,3,2,2,2,5,4,2}。由于數(shù)字2在數(shù)組中出現(xiàn)了5次,超過數(shù)組長度的一半,因此輸出2。如果不存在則輸出0。

2.2.解法一(參考《劍指offer》)

既然出現(xiàn)次數(shù)大于一半,那么中位數(shù)可定是所求的結(jié)果。
通過比較的方式,來求一個數(shù)組中第k大的數(shù),理論最優(yōu)時間復(fù)雜度是O(n),例如五分法,但代碼比較繁瑣。這里采用的方法,最差的時間復(fù)雜度雖然是O(n*n),但平均復(fù)雜度也是O(n)噠。
采用快排的思想,跟快排的不同點(diǎn)是,快排把原數(shù)組分成兩個子數(shù)組后,對這兩個子數(shù)組都進(jìn)行遞歸調(diào)用,而這里只對其中一個感興趣的數(shù)組進(jìn)行遞歸調(diào)用就可以啦,正因?yàn)檫@個,才使得平均時間復(fù)雜度由于快排的O(nlogn)變成了O(n)。

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        if(numbers.empty()) return 0;
        int mid;
        if(numbers.size() < 3)
            mid=numbers[0];
        else{
            m=numbers.size()/2;
            mid=find_middle(numbers,0,numbers.size());
        }
        if(check(numbers,mid))
            return mid;
        return 0;
    }
private:
    int find_middle(vector<int> &numbers, size_t l, size_t r){
        if(r-l ==1) return numbers[l];
        if(r-l ==2){
            if(numbers[l]>numbers[l+1]) swap(numbers[l],numbers[l+1]);
            return numbers[m];
        }
        partial(numbers,l,r);
        size_t i=l;
        size_t j=r-2;
        int pivod=numbers[r-1];
        while(true){
            while(numbers[++i]<pivod){}
            while(numbers[--j]>pivod){}
            if(i<j)
                swap(numbers[i],numbers[j]);
            else
                break;
        }
        swap(numbers[i],numbers[r-1]);
        if(m<i) return find_middle(numbers, l ,i);
        if(m>i) return find_middle(numbers,i+1,r);
        return numbers[m];
    }
    
    void partial(vector<int> &numbers,size_t l, size_t r){
        size_t middle=(l+r)/2;
        if(numbers[l]>numbers[middle]) swap(numbers[l], numbers[middle]);
        if(numbers[l]>numbers[r-1]) swap(numbers[l],numbers[r-1]);
        if(numbers[middle]<numbers[r-1]) swap(numbers[middle],numbers[r-1]);
        swap(numbers[middle],numbers[r-2]);
    }
    
    bool check(vector<int> &numbers,int rv){
        int count=0;
        for(size_t i=0 ; i<numbers.size(); ++i)
            if(rv==numbers[i]) ++count;
        if(count>numbers.size()/2) return true;
        return false;
    }
    
    int m=0;
};

2.3.解法二(參考《劍指offer》)

即使我們把所有其它的數(shù)都去對消這個出現(xiàn)頻率最高的數(shù),最后還是不能把這個數(shù)給對消完,因此就有如下算法:

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        if(numbers.empty()) return 0;
        int rv;
        int count=0;
        for(int i=0; i < numbers.size() ; ++i){
            if(count==0){
                rv=numbers[i];
                ++count;
            }else if(rv==numbers[i])
                ++count;
            else
                --count;
        }
        if(count==0) return 0;
        if(check(numbers,rv)) return rv;
        return 0;
    }
private:
    bool check(vector<int> &numbers,int rv){
        int count=0;
        for(int i=0; i < numbers.size(); ++i)
            if(rv==numbers[i]) ++count;
        return count>numbers.size()/2;
    }
};

3.棧的壓入、彈出序列

3.1.題目

題目描述

輸入兩個整數(shù)序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否為該棧的彈出順序。假設(shè)壓入棧的所有數(shù)字均不相等。例如序列1,2,3,4,5是某棧的壓入順序,序列4,5,3,2,1是該壓棧序列對應(yīng)的一個彈出序列,但4,3,5,1,2就不可能是該壓棧序列的彈出序列。(注意:這兩個序列的長度是相等的)

3.2.解法

構(gòu)造一個輔助棧,例如:12345順序入棧,出棧序列是45321,如果棧是空的,就將1入輔助棧,然后繼續(xù)檢查棧頂元素,發(fā)現(xiàn)它和第二個序列的第一個元素不同,于是繼續(xù)將2入棧,再次檢查棧頂元素是否和4相同,...,當(dāng)壓入4的時候,棧頂元素和4相同,則出棧,并且比較出棧后新的棧頂元素是否和第二個序列的下一個元素,即5相同,顯然3和5不相同,則將第一個序列的5入棧。。。
上述過程實(shí)際上就是在重現(xiàn)出棧入棧的過程。觀察第二個序列的增長,顯然第二個序列增長是最慢的。如果第二個序列不是出棧序列,唯一的情況就是,第一個序列全部都入棧了,但棧頂元素跟第二個序列要處理的元素不同。。。

class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        vector<int> stack1;
        auto ab=pushV.begin();
        auto bb=popV.begin();
        while(bb != popV.end()){
            if( ab == pushV.end() && stack1[stack1.size()-1] != *bb ) return false;
            if(stack1.empty() || stack1[stack1.size()-1] != *bb )
                stack1.push_back(*ab++);
            else{
                stack1.pop_back();
                ++bb;
            }
        }
        return true;
    }
};

4.最小的k個數(shù)

4.1.題目

題目描述

輸入n個整數(shù),找出其中最小的K個數(shù)。例如輸入4,5,1,6,2,7,3,8這8個數(shù)字,則最小的4個數(shù)字是1,2,3,4

4.2.解法一

用stl中的優(yōu)先隊(duì)列
建堆的時間復(fù)雜度是O(n),查找一次的時間復(fù)雜度是O(logn), k次就是O(klogn), 所以總的時間復(fù)雜度是O(n+klogn),空間復(fù)雜度。。。是O(n)

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> return_value;
        if(input.empty() || k <=0 || k>input.size())
            return return_value;
        priority_queue<int,vector<int>,greater<int> > d(input.begin(),input.end());
        while(k--){
            return_value.push_back(d.top());
            d.pop();
        }
        return return_value;
    }
};

4.3.解法二

快排的思想,為前k個數(shù)排序,時間復(fù)雜度應(yīng)該是取決于k吧,最差肯定是O(n*n),平均。。。猜測應(yīng)該是O(n+klogk)

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> rv;
        if( input.empty() || k>input.size() ) return rv;
        partial_sort( input , 0 , input.size() , k );
        for(int i = 0 ; i < k ; ++i) 
            rv.push_back( input[i] );
        return rv;
    }
private:
    void partial_sort(vector<int> &input, int l, int r, int k){
        if(r-l < 2) return;
        if(r-l == 2 ){
            if( input[l] > input[l+1] )
                swap( input[l] , input[l+1] );
            return;
        }
        mid( input, l , r );
        int i=l;
        int j=r-2;
        int pivod = input[r-1];
        while( i < j ) {
            while( input[++i] < pivod ) {}
            while( input[--j] > pivod ) {}
            if( i < j )
                swap( input[i] , input[j] );
            else
                break;
        }
        swap( input[i] , input[r-1] );
        partial_sort( input , l , i , k );
        if( k > i+1 ) partial_sort( input , i+1 , r , k);
    }
    
    void mid(vector<int> &input , int l , int r) {
        int middle = ( l + r )/2;
        if( input[l] > input[r-1] ) swap( input[l] , input[r-1] );
        if( input[l] > input[middle] ) swap( input[l] , input[middle] );
        if( input[r-1] > input[middle] ) swap( input[r-1] , input[middle] );
        swap( input[r-2] , input[middle] );
    }
};

5.最大子數(shù)組的和

class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
        int accum=0;
        int max=(1<<31);
        for( int i=0 ; i < array.size() ; ++i ){
            accum+=array[i];
            if(accum > max ) max=accum;
            if(accum < 0 ) accum=0;
        }
        return max;
    }
};

6.整數(shù)中1出現(xiàn)的次數(shù)

先找規(guī)律:
區(qū)間[0,9]中,出現(xiàn)的次數(shù)是f(1)=1;
區(qū)間[0,99]中,出現(xiàn)的次數(shù)是:f(2)=10f(1)+10,這里,10f(1)表示個位是1的次數(shù),10表示是為是1的次數(shù),例如10,11,...,19這10個數(shù)的十位都是1.
類推,可以知道:
f(1)=1;
f(n)=10f(n-1)+10^(n-1), n>1;
于是f(n)的通項(xiàng):
f(n)=n
10^(n-1)
用g(n)表示0到n中1出現(xiàn)的次數(shù),例如g(612372),那么,顯然有:
g(612372)=6f(5)+100000+g(12372)
再如:
g(112372)=1
f(5)+12372+g(12372)
從高位到低位算起,這個是遞歸的解法。
當(dāng)然,也可以從低位向高位算起,這個時候就不用遞歸了。代碼也更簡單。

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        if(n<=0) return 0;
        int count=0;
        int nn=n;
        if(nn%10) ++count;
        nn/=10;
        int pow=1;
        int idx=1;
        int temp;
        while(nn){
            temp=nn%10;
            count+=temp*idx*pow;
            if(temp>1) count+=pow*10;
            if(temp==1) count+=n%(pow*10)+1;
            ++idx;
            pow*=10;
            nn/=10;
        }
        return count;
    }
};

7.把數(shù)組排成最小的數(shù)

7.1. 題目

題目描述

輸入一個正整數(shù)數(shù)組,把數(shù)組里所有數(shù)字拼接起來排成一個數(shù),打印能拼接出的所有數(shù)字中最小的一個。例如輸入數(shù)組{3,32,321},則打印出這三個數(shù)字能排成的最小數(shù)字為321323。

7.2. 解法(參考《劍指offer》)

定義運(yùn)算a<b,它表示十進(jìn)制的數(shù)ab小于ba,在這種定義下,它是個序,詳細(xì)證明參考《劍指offer》
在上述序的前提下,把數(shù)組從小到大排序,然后依次打印出數(shù)組中的元素,打印的結(jié)果就是最終答案。

class Solution {
public:

    string PrintMinNumber(vector<int> numbers) {
        sort(numbers.begin(), numbers.end(), Solution());
        string s;
        s.reserve(numbers.size()*4);
        for(int i=0 ; i < numbers.size() ; ++i)
            s+=to_string(numbers[i]);
        return s;
    }
    
    bool operator ()(const int &a, const int &b){
        return less_(a,b);
    }
private:
    bool less_(const int &a, const int &b){
        long l=((long)a*pow(bit_(b)))+b;
        long r=((long)b*pow(bit_(a)))+a;
        return l<r;
    }

    int pow(int n){
        int rv=1;
        while(n--){
            rv*=10;
        }
        return rv;
    }

    int bit_(int n){
        int rv=0;
        do{
            ++rv;
            n/=10;
        }while(n);
        return rv;
    }
};

8.丑數(shù)

8.1.題目

題目描述

把只包含因子2、3和5的數(shù)稱作丑數(shù)(Ugly Number)。例如6、8都是丑數(shù),但14不是,因?yàn)樗蜃?。 習(xí)慣上我們把1當(dāng)做是第一個丑數(shù)。求按從小到大的順序的第N個丑數(shù)。

8.2.解法一(O(nlogn)的復(fù)雜度)

維持一個記錄從小到大排列的丑數(shù)矢量,當(dāng)我們要計(jì)算下一個更大的丑數(shù)的時候,不外乎就是把這個矢量的某一個元素乘以2,3,或者5.二分查找的話,這個過程的復(fù)雜度是log(n),因此總的復(fù)雜度就是nlog(n)
注意,二分法一定要處理好邊界條件,例如這里,當(dāng)r-l==2的時候,如果繼續(xù)遞歸,則會死循環(huán)。

class Solution {
public:
    int GetUglyNumber_Solution(int index) {
        if(index<1) return 0;
        ugly.push_back(1);
        while(--index > 0)
            ugly.push_back(find_next());
        return ugly[ugly.size()-1];
    }
private:
    vector<int> ugly;
    int find_next(){
        int m2=find_next_factor(2, 0, ugly.size());
        int m3=find_next_factor(3, 0, ugly.size());
        int m5=find_next_factor(5, 0, ugly.size());
        if(m2>m3) swap(m2, m3);
        if(m2>m5) swap(m2, m5);
        return m2;
    }
    int find_next_factor(int factor, size_t l, size_t r){
        if(r-l==1) return factor*ugly[l];
        if(r-l==2) return factor*ugly[l]>ugly[ugly.size()-1]?factor*ugly[l]:factor*ugly[l+1];
        int middle=(l+r)/2;
        if(factor*ugly[middle] > ugly[ugly.size()-1])
            return find_next_factor(factor, l, middle+1);
        return find_next_factor(factor, middle+1, r);
    }
};

8.3.解法二:對解法一的優(yōu)化(O(n)的復(fù)雜度)

class Solution {
public:
    int GetUglyNumber_Solution(int index) {
        if(index < 1 ) return 0;
        ugly.push_back(1);
        while(--index)
            next_();
        return ugly[ugly.size()-1];
    }
private:
    void next_(){
        int temp2=ugly[next2idx]*2;
        int temp3=ugly[next3idx]*3;
        int temp5=ugly[next5idx]*5;
        int min_temp=temp2;
        if(min_temp>temp3) min_temp=temp3;
        if(min_temp>temp5) min_temp=temp5;
        if(min_temp == temp2) ++next2idx;
        if(min_temp == temp3) ++next3idx;
        if(min_temp == temp5) ++next5idx;
        ugly.push_back(min_temp);
    }
    vector<int> ugly;
    size_t next2idx=0;
    size_t next3idx=0;
    size_t next5idx=0;
};

9.第一個只出現(xiàn)一次的字符

9.1.題目

題目描述

在一個字符串(1<=字符串長度<=10000,全部由字母組成)中找到第一個只出現(xiàn)一次的字符,并返回它的位置

9.2.解法

采用hash的思想,不過,因?yàn)樵氐姆N類太少,這個hashtable足夠大,完全不用考慮如何解決沖突
如果字符沒有粗線,則其在hashtable中的值是-2,如果出現(xiàn)了多次,則為-1,如果只出現(xiàn)一次,則是它所出現(xiàn)的位置。最后遍歷hashtable,得到最靠前的位置

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        size_t table_size='z'-'A'+1;
        size_t begin_='A';
        int hash_table['z'-'A'+1];
        for(int i=0 ; i < table_size ; ++i)
            hash_table[i]=-2;
        for(int i=0 ; i < str.size() ; ++i){
            int idx=str[i]-begin_;
            if( hash_table[idx] == -2)
                hash_table[idx]=i;
            else if(hash_table[idx] >= 0)
                hash_table[idx]=-1;
        }
        
        size_t min=10000;
        for(int i=0 ; i < table_size ; ++i)
            if(hash_table[i] >= 0 && hash_table[i] < min ) min=hash_table[i];
        if( min == 10000 ) return -1;
        return min;
    }
};

10.數(shù)組中的逆序?qū)?/h1>

10.1.題目

題目描述

在數(shù)組中的兩個數(shù)字,如果前面一個數(shù)字大于后面的數(shù)字,則這兩個數(shù)字組成一個逆序?qū)Α]斎胍粋€數(shù)組,求出這個數(shù)組中的逆序?qū)Φ目倲?shù)P。并將P對1000000007取模的結(jié)果輸出。 即輸出P%1000000007

輸入描述:

題目保證輸入的數(shù)組中沒有的相同的數(shù)字
數(shù)據(jù)范圍:
對于%50的數(shù)據(jù),size<=10^4
對于%75的數(shù)據(jù),size<=10^5
對于%100的數(shù)據(jù),size<=2*10^5

10.2.解法

數(shù)據(jù)量達(dá)到了105數(shù)量級,顯然不能用O(n*n)算法,否則運(yùn)算次數(shù)達(dá)到了O(1010),估計(jì)要消耗幾十秒。。。
考慮歸并排序,合并過程中,順便把逆序?qū)Φ臄?shù)量也算出來。
注意每次更新逆序數(shù)都要對1000000007,否則溢出產(chǎn)生的錯誤會讓人莫名其妙。。。(因?yàn)槟嫘驅(qū)Φ臄?shù)據(jù)類型是int型號,所能表示的最大數(shù)大致比2倍的1000000007多出一小點(diǎn)點(diǎn),而2*10^5最高逆序?qū)?shù)是int所能表示的最大正整數(shù)的10倍。。。)

class Solution {
public:
    int InversePairs(vector<int> data) {
        temp=data;
        return merge_sort(data, temp, 0, data.size());
    }
    int merge_sort(vector<int> &data, vector<int> &temp , size_t l , size_t r){
        if(r-l<=1) return 0;
        int middle=(l+r)/2;
        return (
                ( merge_sort(data, temp , l , middle) 
                + merge_sort(data, temp , middle , r) )%1000000007 
                + merge(data, temp , l , r))%1000000007;
    }
    int merge(vector<int> &data, vector<int> &temp , size_t l , size_t r){
        int middle=(l+r)/2;
        int i=l;
        int j=middle;
        int k=l;
        int rv=0;
        while( i < middle && j <r){
            if(data[i] < data[j] ){
                temp[k++]=data[i++];
                rv=(rv+j-middle)%1000000007;
            }else{
                temp[k++]=data[j++];
            }
        }
        while(i != middle){
            temp[k++]=data[i++];
            rv=(rv+j-middle)%1000000007;
        }
        while(j != r){
            temp[k++]=data[j++];
        }
        for(int i=l ; i < r ; ++i )
            data[i]=temp[i];
        return rv;
    }
private:
    vector<int> temp;
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 總結(jié) 想清楚再編碼 分析方法:舉例子、畫圖 第1節(jié):畫圖分析方法 對于二叉樹、二維數(shù)組、鏈表等問題,都可以采用畫圖...
    M_巴拉巴拉閱讀 1,295評論 0 7
  • 第1章 面試的流程 編程時應(yīng)注意的三點(diǎn): 思考清楚再開始編碼; 良好的代碼命名和縮進(jìn)對齊; 能夠單元測試; 現(xiàn)場面...
    codingXue閱讀 573評論 5 0
  • 劍指offer(二) 面試題九:斐波那契數(shù)列 題目一:寫一個函數(shù),輸入n,求斐波那契數(shù)列的第n項(xiàng),裴波納契數(shù)列的定...
    橋?qū)?/span>閱讀 376評論 0 0
  • 注意:本文適用于已刷過題目,想短短幾分鐘快速簡單回顧的情況。沒看過《劍指offer》的讀者建議先閱讀下。 斐波那契...
    FeelsChaotic閱讀 1,886評論 2 8
  • 說明: 本文中出現(xiàn)的所有算法題皆來自牛客網(wǎng)-劍指Offer在線編程題,在此只是作為轉(zhuǎn)載和記錄,用于本人學(xué)習(xí)使用,不...
    秋意思寒閱讀 1,216評論 1 1

友情鏈接更多精彩內(nèi)容