欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

剑指Offer系列

程序员文章站 2022-06-09 10:58:40
...

温故而知新

字符串

替换空格

题目:
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

实现:

class Solution {
public:
    void replaceSpace(char *str,int length) {
        string data;
        for(int i=0;i<length;i++) {
            if(str[i] == ' ')
                data += "%20";
            else
                data += str[i];
        }
        for(int i=0;i<data.length();i++) {
            str[i] = data[i];
        }
        str[data.length()] = '\0';
    }
};

表示数值的字符串

题目:
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串”+100”,”5e2”,”-123”,”3.1416”和”-1E-16”都表示数值。 但是”12e”,”1a3.14”,”1.2.3”,”+-5”和”12e+4.3”都不是。
关键:
1. 可有有符号+, -
2. 整数(0, …, 9)
3. 不能开头为0且长度大于1
4. 小数a.b,满足a符合1, 2,3;b符合2
5. 科学计数法cEd,满足c符合4;d符合1,2,3
代码:

#include <iostream>
#include <string>
using namespace std;

string stripSymbol(string data) {
    return data.length() >= 1 && (data[0] == '-' || data[0] == '+') ? data.substr(1) : data;    
}

bool is0_9(string data) {
    for(auto e : data) {
        if(e < '0' || e > '9')
            return false;
    }
    return data.length() == 0 ? false : true;
}

bool isZ(string data) {
    data = stripSymbol(data);
    if(data.length() > 2 && data[0] == '0') return false;
    return is0_9(data);
}


bool isF(string data) {
    int pos = data.find('.');
    if(pos == -1) {
        return isZ(data);   
    }
    else if(pos == data.length()-1) {
        return false;
    }
    else {
        string part1 = data.substr(0, pos);
        string part2 = data.substr(pos+1);
        return isZ(part1) && is0_9(part2);
    }
}

bool isNumeric(string data) {
    int pos_e = data.find('e');
    int pos_E = data.find('E');
    int pos = pos_e > pos_E ? pos_e : pos_E;
    if(pos == -1) {
        return isF(data);
    }
    else if(pos == data.length()-1) {
        return false;
    }
    else {
        string part1 = data.substr(0, pos);
        string part2 = data.substr(pos+1);
        return isF(part1) && isZ(part2);
    }
}

int main() {
    string test_data[] = {
        "+100",
        "5e2",
        "-123",
        "3.1416",
        "-1E-16",
        "12e",
        "1a3.14",
        "1.2.3",
        "+-5",
        "12e+.3"
    };
    int length = 10;
    for(int i=0;i<length;i++) {
        cout<<isNumeric(test_data[i])<<endl;
    }
}

把字符串转换成整数

题目:
Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。

关键:

实现:

class Solution {
public:
    string noSymbol(string data) {
        if(data.length() >= 1 && (data[0] == '-' || data[0] == '+'))
           data = data.substr(1);
        return data;
    }

    bool isZ(string part2, int &num) {
        num = 0;
        if(part2.length() == 0 || (part2.length() >= 2 && part2[0] == '0'))
           return false;

        for(int i=0;i<part2.length();i++) {
            if(part2[i] > '9' || part2[i] < '0')
                return false;
            num = 10*num + (part2[i]-'0');
        }
        return true;
    }

    int StrToInt(string str) {
        string data = noSymbol(str);
        int num;
        if(isZ(data, num)) {
            if(data.length()==str.length()-1 && str[0] == '-')
                num = -num;
            return num;
        }
        return 0;
    }
};

正则表达式

题目:
请实现一个函数用来匹配包括’.’和’‘的正则表达式。模式中的字符’.’表示任意一个字符,而’‘表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串”aaa”与模式”a.a”和”ab*ac*a”匹配,但是与”aa.a”和”ab*a”均不匹配

关键:
1.递归的终止条件

if(i == data.length() || j == pattern.length()) {
    // 若data = "",pattern = ".*",下面的while循环时必须的
    while(j+1 < pattern.length() && pattern[j+1] == '*') j += 2;
    return (i == data.length()) && (j == pattern.length());
}

2.遇到*号的两种处理方式
1. 跳过*号,pattern指针移动
2. 不跳过*号,继续进行比对,data指针移动

实现:

class Solution {
public:
    bool match(char* str, char* pattern)
    {
        string data = str;
        string patt = pattern;
        return helpMatch(data, patt, 0, 0);
    }

    bool helpMatch(const string &data, const string &pattern, int i, int j) {
        if(i == data.length() || j == pattern.length()) {
            while(j+1 < pattern.length() && pattern[j+1] == '*') j += 2;
            return (i == data.length()) && (j == pattern.length());
        }

        if(j+1 < pattern.length() && pattern[j+1]=='*') {
            return helpMatch(data, pattern, i, j+2) || 
                ((pattern[j] == '.' || data[i] == pattern[j]) && helpMatch(data, pattern, i+1, j));
        }
        else if(pattern[j] == '.' || data[i] == pattern[j]){
            return helpMatch(data, pattern, i+1, j+1);
        }

        return false;
    }
};

翻转单词顺序序列

题目:
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

关键:
end = end == -1 ? str.length() : end;

实现:

class Solution {
public:
    string ReverseSentence(string str) {
        reverse(str.begin(), str.end());
        int begin = 0;
        while(begin < str.length()) {
            int end = str.find(' ', begin);
            end = end == -1 ? str.length() : end;
            reverse(str.begin()+begin, str.begin()+end);
            begin = end + 1;
        }
        return str;
    }
};

左旋转字符串

题目:
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!

关键:
n %= str.length()

实现:

class Solution {
public:
    string LeftRotateString(string str, int n) {
        if(str.length() == 0) return str;
        n %= str.length();
        string part1 = str.substr(0, n);
        string part2 = str.substr(n);
        return part2 + part1;
    }
};

链表

从尾到头打印链表

题目:
输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

实现:

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        vector<int> result;
        while(head != nullptr) {
            result.push_back(head->val);
            head = head->next;
        }
        reverse(result.begin(), result.end());
        return result;
    }
};

合并两个排序的链表

题目:
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

实现:

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
        if(pHead1 == nullptr || pHead2 == nullptr) 
            return pHead1 == nullptr ? pHead2 : pHead1;
        ListNode *head;
        if(pHead1->val < pHead2->val) {
            head = new ListNode(pHead1->val);
            head->next = Merge(pHead1->next, pHead2);
        }
        else {
            head = new ListNode(pHead2->val);
            head->next = Merge(pHead1, pHead2->next);
        }
        return head;
    }
};

反转链表

题目:
输入一个链表,反转链表后,输出新链表的表头。

实现:

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        if(pHead==nullptr) return nullptr;
        if(pHead->next == nullptr) return pHead;
        ListNode* tail = pHead->next;
        ListNode* head = ReverseList(pHead->next);
        tail->next = pHead; pHead->next = nullptr;
        return head;
    }
};

链表中倒数第k个结点

题目:
输入一个链表,输出该链表中倒数第k个结点。

实现:

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        queue<ListNode*> store;
        while(pListHead!=nullptr) {
            store.push(pListHead);
            pListHead = pListHead->next;
            if(store.size() == k+1)
                store.pop();
        }
        return store.size() != k? nullptr : store.front();
    }
};

删除链表中重复的结点

题目:
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
关键:
删除后遍历的开始点,prev和pTravel
实现:

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {
        unordered_map<int, bool> record;
        ListNode *prev = nullptr;
        ListNode *pTravel = pHead;
        while(pTravel!=nullptr) {
            auto pair = record.find(pTravel->val);
            if(pair == record.end())
                record.insert(make_pair(pTravel->val, false));
            else {
                pair->second = true;
                prev->next = pTravel->next;
                delete pTravel;
                pTravel = prev;
            }
            prev = pTravel;
            pTravel = pTravel->next;
        }

        prev = nullptr;
        pTravel = pHead;
        while(pTravel!=nullptr) {
            auto pair = record.find(pTravel->val);
            if(pair->second) {
                if(pTravel == pHead) {
                    pHead = pHead->next;
                    delete pTravel;
                    prev = nullptr;
                    pTravel = pHead;
                }
                else {
                    prev->next = pTravel->next;
                    delete pTravel;
                    pTravel = prev -> next;
                }
            }
            else {
                prev = pTravel;
                pTravel = pTravel->next;
            }
        }
        return pHead;
    }

};

链表环的入口结点

题目:
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

关键:
https://blog.csdn.net/jason_cuijiahui/article/details/77543281中判断一个链表是否有环并确定环的入口

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        ListNode* slow = pHead;
        ListNode* fast = pHead;
        bool isCycle = false;
        while(fast && fast -> next) {
            slow = slow -> next;
            fast = fast -> next -> next;
            if(slow == fast) {
                isCycle = true;
                break;
            }
        }
        if(!isCycle) return nullptr;
        slow = pHead;
        while(slow!=fast) {
            slow = slow -> next;
            fast = fast -> next;
        }
        return slow;
    }
};

两个链表的第一个公共结点

题目:
输入两个链表,找出它们的第一个公共结点。

实现:

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        int len1 = 0;
        ListNode* tmp = pHead1;
        while(tmp!=nullptr) {
            len1++;
            tmp = tmp -> next;
        }

        int len2 = 0;
        tmp = pHead2;
        while(tmp!=nullptr) {
            len2++;
            tmp = tmp -> next;
        }

        if(len1 <= 0|| len2 <= 0) return nullptr;

        if(len2 > len1) {
            tmp = pHead1;
            pHead1 = pHead2;
            pHead2 = tmp;
        }

        while(len1-- != len2) pHead1 = pHead1 -> next;
        while(pHead1!=nullptr && pHead2!=nullptr) {
            if(pHead1 == pHead2) return pHead1;
            pHead1 = pHead1 -> next; pHead2 = pHead2 -> next;
        }
        return nullptr;
    }
};

复杂链表的赋值

题目:
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

实现:

/*
struct RandomListNode {
    int label;
    struct RandomListNode *next, *random;
    RandomListNode(int x) :
            label(x), next(NULL), random(NULL) {
    }
};
*/
class Solution {
public:
    RandomListNode* Clone(RandomListNode* pHead)
    {
        if(pHead==nullptr) return nullptr;
        unordered_map<RandomListNode*, RandomListNode*> map;
        RandomListNode* head = new RandomListNode(pHead->label);
        RandomListNode* orgin = pHead;
        RandomListNode* tmp = head;
        while(pHead!=nullptr) {
            map.insert(make_pair(pHead, tmp));
            tmp->next = pHead->next == nullptr ? nullptr : new RandomListNode(pHead->next->label);
            tmp = tmp->next; pHead = pHead->next;
        }
        tmp = head;
        while(orgin!=nullptr) {
            if(orgin->random!=nullptr)
                tmp->random = map[orgin->random];
            tmp = tmp->next;
            orgin = orgin->next;
        }
        return head;
    }
};

用两个栈实现队列

题目:
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

实现:

class Solution
{
public:
    void push(int node) {
        stack1.push(node);
    }

    int pop() {
        if(stack2.empty()) {
            while(!stack1.empty()) {
                stack2.push(stack1.top());
                stack1.pop();
            }
        }
        int data = -1;
        if(!stack2.empty()) {
            data = stack2.top();
            stack2.pop();
        }
        return data;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};

栈的弹出/弹出序列

题目:
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

实现:

class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        if(pushV.size() != popV.size()) return false;
        stack<int> elements;
        int j = 0;
        for(int i=0;i<pushV.size();i++) {
            if(pushV[i] != popV[j]) 
                elements.push(pushV[i]);
            else
                j++;
        }
        while(!elements.empty()) {
            if(elements.top() != popV[j++])
                return false;
            elements.pop();
        }
        return true;

    }
};

包含min函数的栈

题目:
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。

实现:

class Solution {
private:
    stack<int> mins;
    stack<int> elements;
public:
    void push(int value) {
        mins.empty() || value < mins.top() ? mins.push(value) : mins.push(mins.top());
        elements.push(value);
    }
    void pop() {
        if(!elements.empty()) elements.pop();
        if(!mins.empty()) mins.pop();
    }
    int top() {
        return elements.top();
    }
    int min() {
        return mins.top();
    }
};

回溯

机器人的运动范围

题目:
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

思路:

代码:

#include<vector>
class Solution {
public:
    int movingCount(int threshold, int rows, int cols)
    {
        vector<vector<bool> > visited(rows, vector<bool>(cols));
        return countHelper(threshold, rows, cols, 0, 0, visited);
    }

    int countHelper(const int &threshold, const int &rows, const int &cols, int rPos, int cPos, vector<vector<bool> > &visited) {
        int count = 0;
        if(checkPosOk(threshold, rows, cols, rPos, cPos, visited)) {
            visited[rPos][cPos] = true;
            count = 1 
                + countHelper(threshold, rows, cols, rPos-1, cPos, visited)
                + countHelper(threshold, rows, cols, rPos, cPos-1, visited)
                + countHelper(threshold, rows, cols, rPos+1, cPos, visited)
                + countHelper(threshold, rows, cols, rPos, cPos+1, visited);
        }
        return count;
    }

    bool checkPosOk(const int &threshold, const int &rows, const int &cols, int rPos, int cPos, vector<vector<bool> > &visited) {
        return rPos>=0 && rPos<rows && 
            cPos>=0 && cPos<cols && 
            !visited[rPos][cPos] &&
            (getDigitSum(rPos) + getDigitSum(cPos) <= threshold);
    }

    int getDigitSum(int num) {
        int next = num / 10;
        int result = num % 10;
        while(next!= 0) {
            result += next % 10;
            next = next / 10;
        }
        return result;
    }


};

矩阵中的路径

题目:
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串”bcced”的路径,但是矩阵中不包含”abcb”路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

关键:
1. 对回溯失败的处理,凡是入参时引用类型的都要注意

代码:

#include <cstring>
#include <vector>
using namespace std;
class Solution {
public:
    bool hasPath(char* matrix, int rows, int cols, char* str)
    {
        vector<vector<bool> > visited(rows, vector<bool>(cols));
        int pathIndex = 0;
        for(int i=0;i<rows;i++) {
            for(int j=0;j<cols;j++) {
                if(pathHelper(matrix, rows, cols, str, i, j, pathIndex, visited))
                    return true;
            }
        }
        return false;
    }

    bool pathHelper(char* matrix, int rows, int cols, char* str, int i, int j, int &pathIndex, vector<vector<bool> > &visited) {
        if(pathIndex==(int)strlen(str))
            return true;
        bool pathijOk = false;
        if(i>=0 && i<rows && j>=0 && j<cols && str[pathIndex]==matrix[i*cols+j] && !visited[i][j]) {
            // forward
            visited[i][j] = true;
            pathIndex++;
            pathijOk = pathHelper(matrix, rows, cols, str, i-1, j, pathIndex, visited) ||
                       pathHelper(matrix, rows, cols, str, i+1, j, pathIndex, visited) ||
                       pathHelper(matrix, rows, cols, str, i, j+1, pathIndex, visited) ||
                       pathHelper(matrix, rows, cols, str, i, j-1, pathIndex, visited);

            if(!pathijOk) {
                // if fail, then backward
                pathIndex--;
                visited[i][j] = false;
            }
        }
        return pathijOk;
    }


};

游走

滑动窗口的最大值

题目:
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

注意:
1. size的范围检查
2. size的类型时unsigned int, 当size = 3时,会发生类型转换,出现很诡异的1 - 3 > 0

代码:

class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size)
    {
        deque<int> state;
        vector<int> result;
        if (size > num.size() || size <= 0)
            return result;
        for(int i = 0; i < num.size(); i++) {
            if(!state.empty() && i - (int)size >= state.front())
                state.pop_front();
            while(!state.empty() && num[state.back()] <= num[i])
                state.pop_back();
            state.push_back(i);

            if(i >= (int)size - 1)
                result.push_back(num[state.front()]);
        }
        return result;
    }

};

数据流中的中位数

题目:
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

关键:
1. 维护两个堆,分别为大小堆,我们只需要两个堆的堆顶,为数据流中中间的两个数
2. 注意维护小堆的堆顶 > 大堆的堆顶

代码:

class Solution {
private:
    // minheap[0] > maxheap[0]
    vector<int> minheap;
    vector<int> maxheap;
public:
    void Insert(int num)
    {
        if((minheap.size()+maxheap.size()) % 2 == 0) {
            // insert to minheap
            if(maxheap.size()>0 && num < maxheap[0]) {
                maxheap.push_back(num);
                push_heap(maxheap.begin(), maxheap.end(), less<int>());

                num = maxheap[0];
                pop_heap(maxheap.begin(), maxheap.end(), less<int>());
                maxheap.pop_back();
            }
            minheap.push_back(num);
            push_heap(minheap.begin(), minheap.end(), greater<int>());
        }
        else {
            // insert to maxheap
            if(minheap.size()>0 && num > minheap[0]) {
                minheap.push_back(num);
                push_heap(minheap.begin(), minheap.end(), greater<int>());

                num = minheap[0];
                pop_heap(minheap.begin(), minheap.end(), greater<int>());
                minheap.pop_back();
            }
            maxheap.push_back(num);
            push_heap(maxheap.begin(), maxheap.end(), less<int>());
        }
    }

    double GetMedian()
    {
        int size = minheap.size() + maxheap.size();
        if (size == 0) return -1;
        if (size % 2 == 1) return (double)minheap[0];
        return (double)(minheap[0] + maxheap[0])/2;
    }

};

二叉树

重建二叉树

题目:
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

实现:

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        return solve(pre, vin, 0, pre.size()-1, 0, vin.size()-1);
    }

    TreeNode* solve(const vector<int> &pre, const vector<int> &vin, int pl, int pr, int il, int ir) {
        if(pl>pr || il>ir) return nullptr;
        TreeNode *root = new TreeNode(pre[pl]);
        for(int i=il; i<=ir; i++) {
            if(vin[i] == pre[pl]) {
                int c1 = i -il;
                root->left = solve(pre, vin, pl+1, pl+c1, il, i-1);
                root->right = solve(pre, vin, pl+c1+1, pr, i+1, ir);
                break;
            }
        }
        return root;
    }


};

二叉树的深度

题目:
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

实现:

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    int TreeDepth(TreeNode* pRoot)
    {
        int d;
        Helper(pRoot, d);
        return d;
    }

    void Helper(TreeNode* p, int &depth) {
        if(p==nullptr) {
            depth = 0;
            return;
        }
        int ld, rd;
        Helper(p->left, ld);
        Helper(p->right, rd);
        depth =  1+(ld > rd ? ld : rd);
    }
};

二叉搜索树的第k个结点

题目:
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。

关键:
1. 想要追踪结果需要的是引用变量,所以是void Inorder(TreeNode* p, int &i, int k, TreeNode* &result) 而不是 void void Inorder(TreeNode* p, int &i, int k, TreeNode* result)
2. 同时要注意剪枝if(p == NULL || result != NULL) return;
代码:

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    TreeNode* KthNode(TreeNode* pRoot, int k)
    {
        int i = 0;
        TreeNode* result = NULL;
        Inorder(pRoot, i, k, result);
        return result;
    }

    void Inorder(TreeNode* p, int &i, int k, TreeNode* &result) {
        if(p == NULL || result != NULL) return;
        Inorder(p->left, i, k, result);
        if(++i == k) result = p;
        //cout<<i<<" "<<k<<" "<<result->val<<endl;
        Inorder(p->right, i, k, result);
    }

};

序列化二叉树

题目:
请实现两个函数,分别用来序列化和反序列化二叉树

关键:
1. substract(2)是因为只有在”#,xxxxxxxxxxx”这种情形下才继续
2. data一直在削

实现:
链接中的Serialize and Deserialize Binary Tree

把二叉树打印成多行

题目:
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
代码:

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
        vector<vector<int> > Print(TreeNode* pRoot) {
            vector<vector<int> > result;
            vector<TreeNode*> cursor;
            if(pRoot!=NULL) cursor.push_back(pRoot);
            while(!cursor.empty()) {
                vector<int> element;
                vector<TreeNode*> newCursor;
                for(int i=0;i<cursor.size();i++) {
                    element.push_back(cursor[i]->val);
                    if(cursor[i]->left!=NULL) newCursor.push_back(cursor[i]->left);
                    if(cursor[i]->right!=NULL) newCursor.push_back(cursor[i]->right);
                }
                result.push_back(element);
                cursor = newCursor;
            }
            return result;
        }


};

对称的二叉树

题目:
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
实现:

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    bool isSymmetrical(TreeNode* pRoot)
    {
        if(pRoot == nullptr) return true;
        else return symmHelper(pRoot->left, pRoot->right);
    }

    bool symmHelper(TreeNode* pL, TreeNode* pR) {
        if(pL==nullptr && pR==nullptr) return true;
        if(pL!=nullptr && pR!=nullptr && pL->val == pR->val) {
            auto pLL = pL->left; auto pLR = pL->right;
            auto pRL = pR->left; auto pRR = pR->right;
            return symmHelper(pLL, pRR) && symmHelper(pLR, pRL);
        }
        return false;
    }

};

平衡二叉树

题目:
输入一棵二叉树,判断该二叉树是否是平衡二叉树。

关键:
depth = 1 + (ld > rd) ? ld : rd

实现:

class Solution {
public:
    bool IsBalanced_Solution(TreeNode* pRoot) {
        int d = 0;
        return IsBalanced(pRoot, d);
    }

    bool IsBalanced(TreeNode* p, int &depth) {
        if(p==NULL) {
            depth = 0;
            return true;
        }
        int ld, rd;
        if(IsBalanced(p->left, ld) && IsBalanced(p->right, rd)) {
            int diff = ld -rd;
            if(diff <= 1 && diff >= -1) {
                depth = 1 + (ld > rd ? ld : rd);
                return true;
            }
        }
        return false;
    }
};

二叉树的下一个结点

题目:
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

关键:
1. 如果该结点有右结点,则为右结点的最左结点
2. 如果该结点没有右结点,则为它作为左子树一部分的父结点

实现:

/*
struct TreeLinkNode {
    int val;
    struct TreeLinkNode *left;
    struct TreeLinkNode *right;
    struct TreeLinkNode *next;
    TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {

    }
};
*/
class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode)
    {
        if(pNode==nullptr) return nullptr;

        if(pNode->right!=NULL) {
            auto next = pNode->right;
            while(next->left != nullptr) 
                next = next->left;
            return next;
        }
        else {
            while(pNode->next != nullptr && pNode->next->left != pNode)
                pNode = pNode->next;
            return pNode->next;
        }
    }
};

二叉搜索树与双向链表

题目:
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

实现:

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    TreeNode* Convert(TreeNode* pRootOfTree)
    {
        TreeNode* head = nullptr;
        TreeNode* tail = nullptr;
        solve(pRootOfTree, head, tail);
        return head;
    }

    void solve(TreeNode *pRootOfTree, TreeNode* &head, TreeNode* &tail) {
        if(pRootOfTree == nullptr) return;
        TreeNode *lhead = nullptr, *ltail = nullptr, *rhead = nullptr, *rtail = nullptr;
        solve(pRootOfTree->left, lhead, ltail);
        solve(pRootOfTree->right, rhead, rtail);

        if(ltail != nullptr) ltail->right = pRootOfTree; pRootOfTree->left = ltail;
        if(rhead != nullptr) rhead->left = pRootOfTree; pRootOfTree->right = rhead;

        head = lhead == nullptr ? pRootOfTree : lhead;
        tail = rtail == nullptr ? pRootOfTree : rtail;
    }
};

二叉树中和为某一值的路径

题目:
输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

关键:
递归步不是p==nullptr,如果是路径会出现重复,因为叶子结点左右子结点都是nullptr

实现:

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    static bool cmp(vector<int> &a, vector<int> &b) {
        return a.size()>b.size();
    }
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
        vector<vector<int> > result;
        if(root==nullptr) return result;
        vector<int> path;
        solve(root, path, expectNumber, result);
        sort(result.begin(), result.end(), cmp);
        return result;
    }

    void solve(TreeNode* p, vector<int> &path, int target, vector<vector<int> > &paths) {
        if(p->left == nullptr && p->right == nullptr) {
            if(target == (p->val)) {
                path.push_back(p->val);
                paths.push_back(path);
                path.pop_back();
            }
            return;
        }
        path.push_back(p->val);
        if(p->left!=nullptr) solve(p->left, path, target-(p->val), paths);
        if(p->right!=nullptr) solve(p->right, path, target-(p->val), paths);
        path.pop_back();
    }
};

二叉搜索树的后序遍历序列

题目:
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

实现:

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        if(sequence.size() == 0) return false;
        else return solve(sequence, 0, sequence.size()-1);
    }

    bool solve(vector<int> &sequence, int i, int j) {
        if(i>=j) return true;
        int target = sequence[j];
        int k = i;
        while(k<j && sequence[k++]<target);
        int w = k;
        while(w<j && sequence[w++]>target);
        return w == j && solve(sequence, i, k-1) && solve(sequence,k, j-1); 
    }
};

从上往下打印二叉树

题目:
从上往下打印出二叉树的每个节点,同层节点从左至右打印。

实现:

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        vector<int> result;
        if(root==nullptr) return result;
        queue<TreeNode*> state;
        state.push(root);
        while(!state.empty()) {
            TreeNode* node = state.front();
            result.push_back(node->val);
            state.pop();
            if(node->left!=nullptr) state.push(node->left);
            if(node->right!=nullptr) state.push(node->right);
        }
        return result;
    }
};

二叉树的镜像

题目:
操作给定的二叉树,将其变换为源二叉树的镜像。

二叉树的镜像定义:源二叉树 
            8
           /  \
          6   10
         / \  / \
        5  7 9 11
        镜像二叉树
            8
           /  \
          10   6
         / \  / \
        11 9 7  5

实现:

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    void Mirror(TreeNode *pRoot) {
        if(pRoot==nullptr) return;
        TreeNode *tmp = pRoot->left;
        pRoot->left = pRoot->right;
        pRoot->right = tmp;
        Mirror(pRoot->left);
        Mirror(pRoot->right);
    }
};

树的子结构

题目:
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

实现:

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};*/
class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot1==nullptr || pRoot2==nullptr) return false;
        return IsSubtree(pRoot1, pRoot2) || HasSubtree(pRoot1->left, pRoot2) || HasSubtree(pRoot1->right, pRoot2);

    }

    bool IsSubtree(TreeNode* p1, TreeNode* p2) {
        if(p2 == nullptr) return true;
        if(p1 == nullptr) return false;
        if(p1->val == p2->val)
            return IsSubtree(p1->left, p2->left) && IsSubtree(p1->right, p2->right);
        return false;
    }
};

流取样

从数据流中随机取m个数

题目:
有一个很大很大的输入流,大到没有存储器可以将其存储下来,而且只输入一次,如何从这个输入流中等概率随机取得m个记录。

解答:
开辟一块容纳m个记录的内存区域,对于数据流的第n个记录,以m/n的概率将其留下(前m个先存入内存中,从第m+1个开始),随机替换m个已存在的记录中的一个,这样可以保证每个记录的最终被选取的概率都是相等的。

证明:
数学归纳法
part1:
假设,现在流入第n+1个数据,而前面的n个数据中,每个数据在内存区域的概率为mn

  • 对于新流入数据,其保存在内存区域的概率为mn+1
  • 对于前面的n个数据,其保存在内存区域的概率为mn+1(mnm1m)+(1mn+1)mn=mn+1

part2:
当n=6时
* 对于新流入数据,其保存在内存区域的概率为56
* 对于前面的5个数据,其保存在内存区域的概率为16+56(115)=56

得证

计算

构建乘积数组

题目:

关键:
如何从两个数组找对应的项

实现:

class Solution {
public:
    void helper(vector<int>& left, vector<int>& right, const vector<int>& A) {
        int i=0;
        int j=A.size()-1;
        int mulL = 1;
        int mulR = 1;
        while(i<A.size() && j>=0) {
            mulL *= A[i];
            mulR *= A[j];
            left.push_back(mulL);
            right.push_back(mulR);
            i++; j--;
        }
        reverse(right.begin(), right.end());
    }
    vector<int> multiply(const vector<int>& A) {
        vector<int> left, right;
        helper(left, right, A);
        vector<int> B(A.size());
        for(int i=0;i<B.size();i++) {
            int l = 1;
            int r = 1;
            if(i-1>=0) l = left[i-1];
            if(i+1<B.size()) r = right[i+1];
            B[i] = l * r;
        }
        return B;
    }
};

数组中重复的数字

题目:
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

关键:
因为数字的范围是0 - n-1,所以可以把相应的数字放到数组的相应下标,若有两个相同的数要放到同一个下标下,则说明出现重复了。

实现:

class Solution {
public:
    // Parameters:
    //        numbers:     an array of integers
    //        length:      the length of array numbers
    //        duplication: (Output) the duplicated number in the array number
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    void swap(int numbers[], int i, int j) {
        int tmp = numbers[i];
        numbers[i] = numbers[j];
        numbers[j] = tmp;
    }

    bool duplicate(int numbers[], int length, int* duplication) {
        int i = 0;
        while(i<length) {
            if(numbers[i] != i) {
                if(numbers[i] == numbers[numbers[i]]) {
                    *duplication = numbers[i];
                    return true;
                }
                swap(numbers, i, numbers[i]);
            }
            else
                i++;
        }
        return false;
    }
};

不用加减乘除做加法

题目:
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

关键:
位异或的符号是^

实现:

class Solution {
public:
    int Add(int num1, int num2)
    {
        // x为进位
        int x = (num1 & num2) << 1;
        // y为异或
        int y = num1 ^ num2;
        if(x==0) return y;
        else return Add(x, y);
    }
};

求1+2+3+…+n

题目:
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

实现:

class Solution {
public:
    int Sum_Solution(int n) {
        int result = 1;
        (n == 1) || (result = Sum_Solution(n-1) + n);
        return result;
    }
};

孩子们的游戏(圆圈中最后剩下的数)

题目:
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数….这样下去….直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

关键:
result = (result+m) % i

实现:

class Solution {
public:
    int LastRemaining_Solution(int n, int m)
    {
        if(n == 0 || m == 0) return -1; 
        int result = 0;
        for(int i=2;i<=n;i++) {
            result = (result + m) % i;
        }
        return result;
    }
};

和为S的两个数

题目:
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的

实现:

class Solution {
public:
    vector<int> FindNumbersWithSum(vector<int> array,int sum) {
        vector<int> result(2);
        int i = 0;
        int j = array.size() - 1;
        int mul = array[j] * array[j];
        bool ok = false;
        while(i < j) {
            if(array[i] + array[j] == sum && array[i] * array[j] < mul) {
                ok = true;
                result[0] = array[i];
                result[1] = array[j];
                mul = array[i] * array[j];
            }
            else if(array[i] + array[j] > sum) j--;
            else i++;
        }
        if(!ok) result.clear();
        return result;
    }
};

和为S的连续正数序列

题目:
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

关键:
1. 移动的方向和相应的和的变化
2. 自定义vector排序
3. 类中静态方法

实现:

#include <algorithm>
class Solution {
public:
    static bool Cmp(const vector<int> &a, vector<int> &b) {
        return a.size()>b.size();
    }

    vector<vector<int> > FindContinuousSequence(int target) {
        vector<vector<int> > result;
        int i = 1;
        int j = 2;
        int sum = i + j;
        while(j < target && i < j) {
            if(sum == target) {
                vector<int> e;
                for(int x = i;x<=j;x++) 
                    e.push_back(x);
                result.push_back(e);
                sum -= i++;
            }
            else if(sum > target) {
                sum -= i++;
            }
            else {
                sum += ++j;
            }
        }

        sort(result.begin(), result.end(), Cmp);
        return result;
    }
};

数组中只出现一次的数字

题目:
一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。

关键:
1. index = numXor & ~(numXor-1)
2. (e & index) == index

实现:

class Solution {
public:
    void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
        int numXor = 0;
        for(auto e : data) {
            numXor ^= e;
        }
        int index = numXor & ~(numXor-1);
        *num1 = 0;
        *num2 = 0;
        for(auto e : data) {
            if((e & index) == index)
                *num1 ^= e;
            else
                *num2 ^= e;
        }
        return;
    }
};

第一个只出现一次的字符

题目:
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写)

实现:

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        vector<int> hashTable(256);

        for(auto e : str) {
            hashTable[e]++;
        }

        for(int i=0; i<str.length(); i++) {
            if(hashTable[str[i]] == 1)
                return i;
        }
        return -1;
    }
};

数字在排序数组中出现的次数

题目:
统计一个数字在排序数组中出现的次数。

实现:

class Solution {
public:
    int getFirstK(const vector<int> &data, int i, int j, int k) {
        int mid;
        while(i<=j) {
            mid = (i+j)/2;
            if(data[mid] == k) {
                if(mid == 0 || data[mid-1] != k)
                    return mid;
                else
                    j = mid -1;
            }
            else if(data[mid] > k) {
                j = mid - 1;
            }
            else {
                i = mid + 1;
            }
        }
        return -1;
    }

    int getLastK(const vector<int> &data, int i, int j, int k) {
        int mid;
        while(i<=j) {
            mid = (i+j)/2;
            if(data[mid] == k) {
                if(mid==data.size()-1 || data[mid+1] != k)
                    return mid;
                else
                    i = mid + 1;
            }
            else if(data[mid] > k)
                j = mid - 1;
            else
                i = mid + 1;
        }
        return -1;
    }

    int GetNumberOfK(vector<int> data ,int k) {
        int len = data.size();
        if(len<=0) return 0;
        int first = getFirstK(data, 0, len-1, k);
        int last = getLastK(data, 0, len-1, k);
        return (first!=-1 && last!=-1) ? last-first+1 : 0;
    }
};

数组中的逆序对

题目:
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

关键:
归并排序

实现:

class Solution {
public:
    int InversePairs(vector<int> data) {
        if (data.empty()) return 0;
        vector<int> copy(data);
        return merge(data,0, data.size()-1, copy);
    }

    long long merge(vector<int> &data, int ll, int rr, vector<int> &copy) {
        if(ll >= rr) return 0;
        int lr = (ll + rr)/2;
        int rl = lr + 1, index = ll;
        long long count = merge(copy, ll, lr, data);
        count += merge(copy, rl, rr, data);
        while(ll<=lr && rl<=rr) {
            if(data[ll] > data[rl]) {
                count += lr - ll + 1;
                copy[index++] = data[rl++];
            }
            else {
                copy[index++] = data[ll++];
            }
        }
        while(ll<=lr) copy[index++] = data[ll++];
        while(rl<=rr) copy[index++] = data[rl++];
        return count % 1000000007;
    }

};

丑数

题目:
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

关键:
怎么去除一些不可能的情况

实现:

class Solution {
public:
    int GetUglyNumber_Solution(int n){
        vector<int> ugly(1, 1);
        vector<int> traces(3);
        vector<int> factors(3);
        factors[0] = 2; factors[1] = 3; factors[2] = 5;

        for(int x=1;x<n;x++) {
            int min = INT_MAX;
            for(int i=0;i<3;i++) {
                int num = ugly[traces[i]]*factors[i];
                while(num <= ugly[ugly.size()-1]) {
                    num = ugly[++traces[i]]*factors[i];
                }
                if(num < min)
                    min = num;
            }
            ugly.push_back(min);
        }
        return n == 0 ? 0 : ugly[n-1];
    }
};

把数组排成最小的数

题目:
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

实现:

class Solution {
public:
    static bool cmp(string a, string b) {
        string num1 = a+b;
        string num2 = b+a;
        return num1 < num2;
    }
    string PrintMinNumber(vector<int> numbers) {
        vector<string> strs(numbers.size());
        for(int i=0;i<numbers.size();i++) strs[i] = to_string(numbers[i]); 
        sort(strs.begin(), strs.end(), cmp);
        string ans;
        for(auto e : strs) ans += e;
        return ans;
    }
};

整数中1出现的次数

题目:
求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

实现:

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        int count = 0;
        int level = 1;
        while(level <= n) {
            int top = n / level / 10;
            int target = n / level % 10;
            int tail = n % level;
            if(target > 1) count += (top+1) * level;
            else if(target == 1) count += top * level + tail + 1;
            else count += top * level;
            level *= 10;
        }
        return count;
    }
};

连续子数组的最大和

题目:
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

实现:

class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
        int cadidate = INT_MIN;
        int index = -1;
        for(int i=0;i<array.size();i++) {
            if(array[i] > 0) {
                cadidate = array[i];
                index = i;
                break;
            }
            else if(array[i] > cadidate){
                cadidate = array[i];
            }
        }
        if(index == -1) return cadidate;
        int max = cadidate;
        for(int i=index+1;i<array.size();i++) {
            cadidate += array[i];
            if(cadidate < 0) {
                cadidate = 0;
                continue;
            }
            if(cadidate > max)
                max = cadidate;
        }
        return max;
    }
};

最小的K个数

题目:
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

实现:

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> result;
        if(input.size()<k) return result;
        priority_queue<int> q;
        for(auto e : input) {
            q.push(e);
            if(q.size() == k+1)
                q.pop();
        }
        while(!q.empty()) {
            result.push_back(q.top());
            q.pop();
        }
        reverse(result.begin(), result.end());
        return result;
    }
};

数组中出现次数超过一半的数字

题目:
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

实现:

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        int count = 0;
        int target = 0;
        for(auto e : numbers) {
            if(count == 0) {
                target = e;
                count++;
            }
            else if(e == target) count++;
            else count--;
        }
        count = 0;
        for(auto e : numbers) {
            if(target == e && count++ == numbers.size()/2) 
              break;
        }
        return count > numbers.size()/2 ? target : 0;
    }

};

字符串的排列

题目:
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

实现:

class Solution {
public:
    bool isSwap(string &data, int i, int j) {
        for(int k=i;k<j;k++) {
            if(data[k] == data[j])
                return false;
        }
        return true;
    }
    void swap(string &data, int i, int j) {
        if(i == j) return;
        char x = data[i];
        data[i] = data[j];
        data[j] = x;
    }

    vector<string> Permutation(string str) {
        vector<string> result;
        if(str.length() == 0) return result;
        solve(str, 0, result);
        sort(result.begin(), result.end());
        return result;
    }

    void solve(string &str, int index, vector<string> &result) {
        if(index == str.length()) {
            result.push_back(str);
            return;
        }
        for(int i=index; i<str.length(); i++) {
            if(isSwap(str, index, i)) {
                swap(str, index, i);
                solve(str, index + 1, result);
                swap(str, index, i);
            }
        }
    }
};

顺时针打印矩阵

题目:
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 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.

实现:

class Solution {
public:
    vector<int> printMatrix(vector<vector<int> > matrix) {
        vector<int> result;
        if(matrix.size()==0 || matrix[0].size()==0) return result;
        helper(matrix, 0, matrix.size(), matrix[0].size(), result);
        return result;
    }

    void helper(const vector<vector<int> > &matrix, int i, int rows, int cols, vector<int> &result) {
        if(rows==0 || cols==0) return;
        if(rows==1) {
            for(int j=i; j-i<cols; j++)
                result.push_back(matrix[i][j]);
            return;
        }
        if(cols==1) {
            for(int k=i; k-i<rows; k++)
                result.push_back(matrix[k][i]);
            return;
        }
        int i1 = i, j1 = i;
        int i2 = i, j2 = i+cols-1;
        int i3 = i+rows-1, j3 = i;
        int i4 = i+rows-1, j4 = i+cols-1;
        for(int k = i1; k <= j2; k++)
            result.push_back(matrix[i1][k]);
        for(int k = i1+1; k <= i3; k++)
            result.push_back(matrix[k][j2]);
        for(int k = j4-1; k>=j3; k--)
            result.push_back(matrix[i3][k]);
        for(int k = i3-1; k>=i1+1; k--)
            result.push_back(matrix[k][j1]);
        helper(matrix, i+1, rows-2, cols-2, result);
    }
};

调整数组顺序使奇数位于偶数前面

题目:
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

实现:

class Solution {
public:
    void reOrderArray(vector<int> &array) {
        vector<int> copy(array);
        int i=0;
        for(auto x : copy) {
            if(x % 2 == 1)
                array[i++] = x;
        }
        for(auto x : copy) {
            if(x % 2 == 0)
                array[i++] = x;
        }
        return;
    }
};

数值的整数次方

题目:
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

实现:

class Solution {
public:
    double Power(double base, int exponent) {
        if(exponent == 0) return 1;
        else if(exponent > 0) return PositivePower(base, exponent);
        else return 1/PositivePower(base, -exponent);
    }
    double PositivePower(double base, int exponent) {
        if(exponent == 1) return base;
        double e = PositivePower(base, exponent/2);
        return exponent % 2 == 0 ? e*e : e*e*base;
    }
};

二进制中1的个数

题目:
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

实现:

class Solution {
public:
     int NumberOf1(int n) {
         int count = 0;
         while(n != 0) {
             count++;
             n = n & (n-1);
         }
         return count;
     }
};

矩形覆盖

题目:
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

实现:

class Solution {
public:
    int rectCover(int number) {
        if(number<=0) return 0;
        if(number == 1 || number == 2) return number;
        return rectCover(number-1) + rectCover(number-2);
    }
};

变态跳台阶

题目:
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

实现:

class Solution {
public:
    int jumpFloorII(int number) {
        if(number <= 0) return 0;
        if(number == 1) return 1;
        return 2*jumpFloorII(number-1);
    }
};

跳台阶

题目:
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

实现:

class Solution {
public:
    int jumpFloor(int number) {
        if(number<=0) return 0;
        if(number==1 || number==2) return number;
        return jumpFloor(number-1)+jumpFloor(number-2);
    }
};

斐波拉契数列

题目:
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
n<=39

实现:

class Solution {
public:
    int Fibonacci(int n) {
        if(n <= 0) return 0;
        if(n == 1 || n == 2) return 1;
        int t1 = 1, t2 = 1;
        int t3;
        for(int j=2; j < n; j++) {
            t3 = t1 + t2;
            t1 = t2;
            t2 = t3;
        }
        return t3;
    }
};

旋转数组的最小数字

题目:
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

实现:

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        if(rotateArray.size()==0) return 0;
        return solve(rotateArray, 0, rotateArray.size()-1);
    }

    int solve(const vector<int> &data, int i, int j) {
        for(; i < j && data[i] == data[i+1]; i++);
        for(; i < j && data[j] == data[j-1]; j--);
        if(data[i] < data[j] || i == j) return data[i];
        if(i+1 == j) return data[j];
        int mid = (i+j)/2;
        return data[mid]>data[j] ? solve(data, mid, j) : solve(data, i, mid);
    }
};

二维数组中的查找

题目:
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

实现:

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        if(array.size()==0) return false;
        int i = 0, j = array[0].size()-1;
        while(j>=0 && i<=array.size()-1) {
            if(target > array[i][j])
                i++;
            else if(target < array[i][j])
                j--;
            else
                return true;
        }
        return false;
    }
};
相关标签: 2018 算法