剑指offer--总结记录

2.3.2字符串

面试题4:二维数组中的查找

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

思路:在查找的过程中,首先选取数组中右上角的数字,如果要查找的数字不在数组的右上角,则每一次都在数组的查找范围中剔除一行或者一列,这样每一步都可以缩小查找范围,直到找到要查找的数字,或者查找范围为空。(书上的思路很清晰)

代码:

#include <iostream>
using namespace std;
bool Find(int* matrix,int rows, int columns,int number)
{
    bool found = false;
    if(matrix != nullptr && rows > 0 && columns > 0){
        int row = 0;
        int column = columns -1 ;
        while (row < rows && column >= 0){
            if (matrix[row*columns+column] == number){
                found = true;
                break;
            }
            else if(matrix[row*columns+column] > number)
                --column;
            else
                ++row;

        }
    }
    return found;
}
void Test(char* testName, int* matrix, int rows, int columns, int number, bool expected)
{
    if(testName != nullptr)
        printf("%s begins: ", testName);

    bool result = Find(matrix, rows, columns, number);
    if(result == expected)
        printf("Passed.\n");
    else
        printf("Failed.\n");
}

//  1   2   8   9
//  2   4   9   12
//  4   7   10  13
//  6   8   11  15
// 要查找的数在数组中

int main()
{
    int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
    Test("Test1", (int*)matrix, 4, 4, 7, true);
}

 当然也可以首先选取数组中左下角的数字,这样子的话程序的条件要做一定的修改,但是基本的逻辑是不变的。PS:怎么能够利用二维数组的行和列来表示某个位置的元素,是比较关键的问题。

bool Find(int* matrix,int rows, int columns,int number)
{
    bool found = false;
    if(matrix != nullptr && rows > 0 && columns > 0){
        int row = rows - 1;
        int column = 0 ;
        while (row < rows && column >= 0){
            if (matrix[row*columns+column] == number){
                found = true;
                break;
            }
            else if(matrix[row*columns+column] < number)
                ++column;
            else
                --row;
        }
    }
    return found;
}

 字符串数组和字符指针的区别:

#include <iostream>
using namespace std;

int main()
{
    char str1[] = "hello world";
    char str2[] = "hello world";

    char *str3 = "hello world";//同样的一个字符串,储存在常量区
    char *str4 = "hello world";

    if(str1 == str2)
    {
        cout<<"str1 and str2 are same"<<endl;
    }
    else{
        cout<<"str1 and str2 are not same"<<endl;
    }
    if(str3 == str4){
        cout<<"str3 and str4 are same"<<endl;
    }
    else{
        cout<<"str3 and str4 are not same"<<endl;
    }
}

 存的都是“hello world”,字符数组st1和str2是不一样的,字符指针的st3和str4指向的是同一个字符常量区。


面试题5:替换空格

题目:请实现一个函数,把字符串中的每个空格替换成“%20”,例如输入“we are happy.”,则输出“we%20are%20happy.”

#include <iostream>
using namespace std;

void ReplaceBlank(char string[],int length)
{
    if(string == nullptr || length <= 0)
        return;
    int originalLength = 0;
    int numberOfBlank = 0;
    int i = 0;
    while(string[i] != '\0'){
        ++originalLength;
        if(string[i] == ' ')//判断是否四空格,逻辑表达式啊
            ++numberOfBlank;

        ++i;
    }
    int newLength = originalLength + numberOfBlank*2;
    if(newLength > length)
        return;
    int indexOfOriginal = originalLength;
    int indexOfNew = newLength;
    while (indexOfOriginal >= 0 && indexOfNew >indexOfOriginal){
        if(string[indexOfOriginal] == ' ')
        {
            string[indexOfNew--] = '0';
            string[indexOfNew--] = '2';
            string[indexOfNew--] = '%';
        }
        else
            string[indexOfNew--] = string[indexOfOriginal];
        --indexOfOriginal;
    }
}

int main()
{
    char str[50] = "we are happy.";
    ReplaceBlank(str,50);
    cout<<str<<endl;
}

上面的测试程序只是一个简答的测试用例,不够完整。

2.3.3链表

不带头结点链表实现(插入和删除节点)

#include <iostream>
using namespace std;
// 首先写一个链表节点
struct ListNode{
    int m_nValue;
    ListNode* m_pNext;
};
// 往链表的末尾添加一个节点
void AddToTail(ListNode** pHead, int value)
{
    ListNode* pNew = new ListNode();
    pNew->m_nValue = value;
    pNew->m_pNext = nullptr;
    if(*pHead == nullptr){ //判断是否是空链表
        *pHead = pNew;
    }
    else{
        ListNode* pNode = *pHead;
        while (pNode->m_pNext != nullptr)// 找到要插入的位置
            pNode = pNode->m_pNext;
        pNode->m_pNext = pNew;
    }
}

void RemoveNode(ListNode**pHead, int value)
{
    if(pHead == nullptr || *pHead == nullptr)
        return;
    ListNode* pToBeDeleted = nullptr;
    if((*pHead)->m_nValue == value){
        pToBeDeleted = *pHead;
        *pHead = (*pHead)->m_pNext;
    }
    else{
        ListNode* pNode = *pHead;
        while (pNode->m_pNext != nullptr && pNode->m_pNext->m_nValue != value)
            pNode = pNode->m_pNext;
        if(pNode->m_pNext != nullptr && pNode->m_pNext->m_nValue == value){
            pToBeDeleted = pNode->m_pNext;
            pNode->m_pNext = pNode->m_pNext->m_pNext;
        }
    }
    if(pToBeDeleted != nullptr){
        delete pToBeDeleted;
        pToBeDeleted = nullptr;
    }
}
int main()
{
    ListNode**p = nullptr;
    ListNode*phead = nullptr;
    p = &phead;
    AddToTail(p,5);
    AddToTail(p,4);
    AddToTail(p,12);
    AddToTail(p,11);
    AddToTail(p,0);
    RemoveNode(p, 0);
    return 0;
}

面试题6:从尾到头打印链表

解法一:用栈来辅助实现

void PrintListReversingly_Iteratively(ListNode* pHead)
{
    std::stack<ListNode*>nodes;

    ListNode* pNode = pHead;
    while (pNode != nullptr){
        nodes.push(pNode);
        pNode = pNode->m_pNext;
    }
    while (!nodes.empty()){
        pNode = nodes.top();
        printf("%d\t",pNode->m_nValue);
        nodes.pop();
    }
}

解法二:用递归来实现

void PrintListReversingly_Recursively(ListNode* pHead)
{
    if(pHead != nullptr){
        if(pHead->m_pNext != nullptr){
            PrintListReversingly_Recursively(pHead->m_pNext);
        }
        printf("%d\t",pHead->m_nValue);
    }
}

面试题7:重建二叉树

#include <iostream>
#include <exception>
struct BinaryTreeNOde
{
    int my_nValue;
    BinaryTreeNOde* m_pLeft;
    BinaryTreeNOde* m_pRight;
};
BinaryTreeNOde* ConstructCore(int* sartPreorder, int* endPreorder, int* startInorder, int* endInorder);
BinaryTreeNOde * Construct(int* preorder, int* inorder, int length)
{
    if(preorder == nullptr || inorder == nullptr || length <= 0 )
        return nullptr;
    return ConstructCore(preorder,preorder + length -1,inorder,inorder+length-1);
}

BinaryTreeNOde* ConstructCore(int* startPreorder, int* endPreorder, int* startInorder, int* endInorder)
{
    int rootValue = startPreorder[0];
    BinaryTreeNOde* root = new BinaryTreeNOde();
    root->my_nValue = rootValue;
    root->m_pLeft = root->m_pRight = nullptr;

    if(startPreorder == endPreorder){
        if(startInorder == endInorder && *startPreorder == *startInorder)
            return root;
        else
            throw std::exception();//这个异常的抛出是怎么回事?
    }

    int* rootInorder = startInorder;
    while (rootInorder <= endInorder && *rootInorder != rootValue)
        ++rootInorder;
    if(rootInorder == endInorder && *rootInorder != rootValue)
        throw std::exception();

    int LeftLength = rootInorder - startInorder;
    int *leftPreorderEnd = startPreorder + LeftLength;

    if(LeftLength > 0)
    {
        root->m_pLeft = ConstructCore(startPreorder+1,leftPreorderEnd,startInorder,rootInorder-1);
    }

    if(LeftLength < endPreorder - startPreorder)
    {
        root->m_pRight = ConstructCore(leftPreorderEnd+1,endPreorder,rootInorder+1,endInorder);
    }
    return root;

}

面试题8:二叉树的下一个节点

#include <iostream>
struct BinaryTreeNode
{
    int                    m_nValue;
    BinaryTreeNode*        m_pLeft;
    BinaryTreeNode*        m_pRight;
    BinaryTreeNode*        m_pParent;
};
BinaryTreeNode* GetNext(BinaryTreeNode* pNode)
{
    if(pNode == nullptr)
        return nullptr;
    BinaryTreeNode* pNext = nullptr;
    if(pNode->m_pRight != nullptr)
    {
        BinaryTreeNode*pRight = pNode->m_pRight;
        while(pRight->m_pLeft != nullptr)
            pRight = pRight->m_pLeft;
        pNext = pRight;
    }
    else if(pNode->m_pParent != nullptr)
    {
        BinaryTreeNode* pCurrent = pNode;
        BinaryTreeNode* pParent = pNode->m_pParent;
        while(pParent != nullptr && pCurrent == pParent->m_pRight)
        {
            pCurrent = pParent;
            pParent = pParent->m_pParent;
        }
        pNext = pParent;
    }
    return pNext;
}

上面的代码根据当前节点有没有右子树,分了两种情况。没有右子树又分了两种情况,当前节点为父节点的左孩子,和当前节点为父亲节点的右孩子。

2.3.5栈和队列

面试题9:用两个栈实现队列

题目:用两个栈实现一个队列。队列的声明如下,请实现它的两个函数appendTail和deleteHead,分别完成在队列尾部插入节点和在队列头部删除节点的功能。

#include <iostream>
#include <stack>
#include <exception>
using namespace std;
//用两个栈来实现一个队列的代码
template <typename T>class CQueue
{
public:
    CQueue(void){};
    ~CQueue(void){};
    void appendTail(const T& element);
    T deleteHead();

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

template <typename T> void CQueue<T>::appendTail(const T &element)
{
    stack1.push(element);
}

template <typename T>T CQueue<T>::deleteHead()
{
    if(stack2.size() <= 0){
        while (stack1.size()>0){
            T& data = stack1.top();
            stack1.pop();
            stack2.push(data);
        }
    }

    if(stack2.size() == 0)
        throw new exception();

    T head = stack2.top();
    stack2.pop();

    return head;
}

int main()
{

    CQueue<int > myqueue;
    for (int i = 0; i < 5; ++i) {
        myqueue.appendTail(i);
    }
    for (int j = 0; j < 5; ++j) {
        int k = myqueue.deleteHead();
        cout<< k <<" ";
    }

}

附加题:用两个队列实现一个栈

#include <iostream>
#include <queue>
#include <exception>
using namespace std;
//用两个队列来实现一个栈
template <typename T>class CStack
{
public:
    CStack(void){};
    ~CStack(void){};
    void appendTail(const T& element);
    T deleteHead();

private:
    queue<T> queue1;
    queue<T> queue2;
};

template <typename T> void CStack<T>::appendTail(const T &element)
{
    if(!queue1.empty() && !queue2.empty()){

    }
    else{
        if(queue1.empty()){
            queue2.push(element);
        }
        else{
            queue1.push(element);
        }

    }
}

template <typename T>T CStack<T>::deleteHead()
{
    if (queue1.empty() && queue2.empty()) {
        //异常情况
        throw new exception();
    }
    T head;
    if (!queue1.empty()) {
        while (queue1.size() > 1) {
            T &data = queue1.front();
            queue1.pop();
            queue2.push(data);
        }
        head = queue1.front();
        queue1.pop();
    }
    else {
        while(queue2.size() > 1){
            T& data = queue2.front();
            queue2.pop();
            queue1.push(data);
        }
        head = queue2.front();
        queue2.pop();
    }
    return head;
}
void test(char actual, char expected)
{
    if (actual == expected)
        printf("Test passed.\n");
    else
        printf("Test failed.\n");
}


int main()
{

    CStack<char> stack;

    stack.appendTail('a');
    stack.appendTail('b');
    stack.appendTail('c');

    char head = stack.deleteHead();
    test(head, 'c');

    head = stack.deleteHead();
    test(head, 'b');

    stack.appendTail('d');
    head = stack.deleteHead();
    test(head, 'd');

    stack.appendTail('e');
    head = stack.deleteHead();
    test(head, 'e');

    head = stack.deleteHead();
    test(head, 'a');

    //空栈删除,触发异常
    //head = stack.deleteHead();

    getchar();
    return 0;

}


2.4.1递归和循环

#include <iostream>
using namespace std;
// 1+2+3+...+n的两种实现方式递归、循环

int AddFrom1ToN_Recursive(int n)
{
    return n<=0 ? 0 : n + AddFrom1ToN_Recursive(n-1);
}

int AddFrom1ToN_Iterative(int n)
{
    int result = 0;
    for (int i = 0; i <= n ; ++i) {
        result += i;
    }
    return result;
}

int main()
{
    cout<<AddFrom1ToN_Recursive(100)<<endl;
    cout<<AddFrom1ToN_Iterative(100)<<endl;
    return 0;
}

面试题10:斐波那契数列

题目一:求菲波那契数列的第N项。

#include <iostream>
using namespace std;

long long Fibonacci_1(unsigned int n)
{
    if(n <= 0){
        return 0;
    }
    if(n == 1){
        return 1;
    }
    return Fibonacci_1(n - 1 ) + Fibonacci_1(n - 2);
}
int main()
{
    cout<<Fibonacci_1(100)<<endl;
    return 0;
}

PS:按照作者的说法,试了一下求第100项的时候,真的是巨慢!!!此方法有大量的重复计算,并且递归的成熟也比较多。

#include <iostream>
using namespace std;
//求斐波那契数列的第N项
long long Fibonacci_2(unsigned int n)
{
    int result[2] = {0 , 1};
    if(n < 2){
        return result[n];
    }
    long long fibNMinusOne = 1;
    long long fibNMinusTwo = 0;
    long long fibN = 0;
    for (unsigned int i = 2; i <= n ; ++i) {
        fibN = fibNMinusTwo + fibNMinusOne;
        fibNMinusTwo = fibNMinusOne;
        fibNMinusOne = fibN;
    }
    return fibN;
 }
int main()
{
    cout<<Fibonacci_2(100)<<endl;
    return 0;
}

PS:没有比较就没有伤害,这种自下而上的方式,真的算的快多了。。。斐波那契数列的应用问题:青蛙跳台阶问题;用2*1小矩形覆盖2*8的大矩形,总共有多少种方法?

2.4.2查找和排序

剑指OFFER——快排的代码:

#include <stdio.h>
#include <stdlib.h>

int RandomInRange(int min, int max)
{
    int random = rand() % (max - min + 1) + min;
    return random;
}

void Swap(int* num1, int* num2)
{
    int temp = *num1;
    *num1 = *num2;
    *num2 = temp;
}

int Partition(int data[], int length, int start, int end)
{
    if(data == NULL || length <= 0 || start < 0 || end >= length) {
        return -1;
    }

    int index = RandomInRange(start, end);
    Swap(&data[index], &data[end]);

    int small = start - 1;
    for(index = start; index < end; ++ index)
    {
        if(data[index] < data[end])
        {
            ++ small;
            if(small != index)
                Swap(&data[index], &data[small]);
        }
    }

    ++ small;
    Swap(&data[small], &data[end]);

    return small;
}

void QuickSort (int data[], int length, int start, int end) {
    if (start == end) return;

    int index = Partition(data, length, start, end);
    if (index > start)
        QuickSort(data, length, start, index-1);
    if (index < end)
        QuickSort(data, length, index+1, end);
}

int main(void)
{
    int str[] =  {49, 38, 65, 97, 76, 13, 27, 49};
    QuickSort(str, 8, 0, 7);
    for (int i : str) {
        printf("%d ", i);
    }
    printf("\n");

    return 0;
}

对公司中所有员工的年龄进行排序:

#include <iostream>
using namespace std;
// 对一个公司中所有员工的年龄进行排序
/*parameters:
 * ages[]:the age of stuff
 * length:the length of the ages[]
 */
void SortAges(int ages[], int length)
{
    //对数组ages[]进行判断
    if(ages == nullptr || length <= 0)
        return;
    const int oldestAge = 99;
    //公司员工的年龄在0~99岁,所以timesOfAge[]数组的大小为100
    int timesOfAge[oldestAge + 1];
    //初始化数组timesOfAge[]
    for (int i = 0; i <= oldestAge ; ++i) {
        timesOfAge[i] = 0;
    }
    //ages[]中的元素为i就在timesOfAge[i]上加一,来统计年龄为i的人数
    for (int j = 0; j < length; ++j) {
        int age = ages[j];
        if(age < 0 || age > oldestAge)
            throw new std::exception();
        ++timesOfAge[age];
    }
    //遍历timesOfAge[]数组,某个年龄出现了多少次,就在数组ages[]里面设置几次年龄
    int index = 0;
    for (int k = 0; k <=oldestAge ; ++k) {
        for (int i = 0; i < timesOfAge[k]; ++i) {
            ages[index] = k;
            ++index;
        }
    }
}
int main()
{
   int arr[] = {22,40,18,19,50,21,22,33,44,43};
   int length = sizeof(arr) / sizeof(int);
   SortAges(arr,length);
    for (int i = 0; i < length; ++i) {
        cout<<arr[i]<<" ";
    }

}

面试题11:旋转数组的最小数字

数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,求该数组的最小值。

#include <iostream>
#include <exception>
using namespace std;
int MinInOrder(int* numbers, int index1, int index2);

int Min(int* numbers, int length)
{
    if(numbers == nullptr || length <= 0)
        throw new exception();

    int index1 = 0;
    int index2 = length - 1;
    int indexMid = index1;
    while (numbers[index1] >= numbers[index2])
    {
        if(index2 - index1 == 1){
            indexMid = index2;
            break;
        }
        indexMid = (index1 + index2) / 2;
        if(numbers[index1] == numbers[index2] && numbers[indexMid] == numbers[index1])
            return MinInOrder(numbers, index1, index2);

        if(numbers[indexMid] >= numbers[index1])
            index1 = indexMid;
        else if(numbers[indexMid <= numbers[index2]])
            index2 = indexMid;
    }
    return numbers[indexMid];
}

int MinInOrder(int* numbers, int index1, int index2)
{
    int result = numbers[index1];
    for (int i = index1 + 1; i <= index2 ; ++i) {
        if(result > numbers[i])
            result = numbers[i];
    }
    return result;
}

// ====================测试代码====================
void Test(int* numbers, int length, int expected)
{
    int result = 0;
    try
    {
        result = Min(numbers, length);

        for(int i = 0; i < length; ++i)
            printf("%d ", numbers[i]);

        if(result == expected)
            printf("\tpassed\n");
        else
            printf("\tfailed\n");
    }
    catch (...)
    {
        if(numbers == nullptr)
            printf("Test passed.\n");
        else
            printf("Test failed.\n");
    }
}

int main(int argc, char* argv[])
{
    // 典型输入,单调升序的数组的一个旋转
    int array1[] = { 3, 4, 5, 1, 2 };
    Test(array1, sizeof(array1) / sizeof(int), 1);

    // 有重复数字,并且重复的数字刚好的最小的数字
    int array2[] = { 3, 4, 5, 1, 1, 2 };
    Test(array2, sizeof(array2) / sizeof(int), 1);

    // 有重复数字,但重复的数字不是第一个数字和最后一个数字
    int array3[] = { 3, 4, 5, 1, 2, 2 };
    Test(array3, sizeof(array3) / sizeof(int), 1);

    // 有重复的数字,并且重复的数字刚好是第一个数字和最后一个数字
    int array4[] = { 1, 0, 1, 1, 1 };
    Test(array4, sizeof(array4) / sizeof(int), 0);

    // 单调升序数组,旋转0个元素,也就是单调升序数组本身
    int array5[] = { 1, 2, 3, 4, 5 };
    Test(array5, sizeof(array5) / sizeof(int), 1);

    // 数组中只有一个数字
    int array6[] = { 2 };
    Test(array6, sizeof(array6) / sizeof(int), 2);

    // 输入nullptr
    Test(nullptr, 0, 0);

    return 0;
}

面试题12:矩阵中的路径

题目:请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵任意一格开始,每一步都可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。

#include <cstdio>
#include <cstring>
#include <stack>

using namespace std;
bool hasPathCore(const char* matrix, int rows, int cols, int row, int col, const char* str, int& pathLength, bool* visited);

bool hasPath(const char* matrix, int rows, int cols, const char* str)
{
	// 数组、字符串判空
    if(matrix == nullptr || rows < 1 || cols < 1 || str == nullptr)
        return false;
	
    bool *visited = new bool[rows * cols];// 是否路过节点的布尔矩阵
    memset(visited, 0, rows * cols);// 初始化布尔矩阵

    int pathLength = 0;// 从字符串首开始
	
    for (int row = 0; row < rows; ++row) {
        for (int col = 0; col < cols; ++col) {
            if(hasPathCore(matrix, rows, cols, row, col, str, pathLength, visited)){
                return true;
            }
        }
    }
    delete[] visited;
    return false;
}

bool hasPathCore(const char* matrix, int rows, int cols, int row, int col, const char* str, int& pathLength, bool* visited)
{
    if(str[pathLength] == '\0')
        return true;

    bool hasPath = false;
    int index = row*cols + col;
    if(row >= 0 && row < rows && col >= 0 && col < cols && matrix[index] == str[pathLength] && !visited[index]) {
        ++pathLength;
        visited[index] = true;
		// 矩阵第n字符位置(row, col)上下左右四个点是否能找到字符串中n+1个字符
        hasPath = hasPathCore(matrix, rows, cols, row, col - 1, str, pathLength, visited)
            || hasPathCore(matrix, rows, cols, row - 1, col, str, pathLength, visited)
            || hasPathCore(matrix, rows, cols, row, col + 1, str, pathLength, visited)
            || hasPathCore(matrix, rows, cols, row + 1, col, str, pathLength, visited);
		// 找不到第n+1个字符,则回溯重新定位第n个字符
        if (!hasPath) {
            --pathLength;
            visited[index] = false;
        } 
    }
    return hasPath;
}

// ====================测试代码====================
void Test(const char* testName, const char* matrix, int rows, int cols, const char* str, bool expected)
{
    if(testName != nullptr)
        printf("%s begins: ", testName);

    if(hasPath(matrix, rows, cols, str) == expected)
        printf("Passed.\n");
    else
        printf("FAILED.\n");
}

//ABTG
//CFCS
//JDEH

//BFCE
void Test1()
{
    const char* matrix = "ABTGCFCSJDEH";
    const char* str = "BFCE";

    Test("Test1", (const char*) matrix, 3, 4, str, true);
}

//ABCE
//SFCS
//ADEE

//SEE
void Test2()
{
    const char* matrix = "ABCESFCSADEE";
    const char* str = "SEE";

    Test("Test2", (const char*) matrix, 3, 4, str, true);
}

//ABTG
//CFCS
//JDEH

//ABFB
void Test3()
{
    const char* matrix = "ABTGCFCSJDEH";
    const char* str = "ABFB";

    Test("Test3", (const char*) matrix, 3, 4, str, false);
}

//ABCEHJIG
//SFCSLOPQ
//ADEEMNOE
//ADIDEJFM
//VCEIFGGS

//SLHECCEIDEJFGGFIE
void Test4()
{
    const char* matrix = "ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS";
    const char* str = "SLHECCEIDEJFGGFIE";

    Test("Test4", (const char*) matrix, 5, 8, str, true);
}

//ABCEHJIG
//SFCSLOPQ
//ADEEMNOE
//ADIDEJFM
//VCEIFGGS

//SGGFIECVAASABCEHJIGQEM
void Test5()
{
    const char* matrix = "ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS";
    const char* str = "SGGFIECVAASABCEHJIGQEM";

    Test("Test5", (const char*) matrix, 5, 8, str, true);
}

//ABCEHJIG
//SFCSLOPQ
//ADEEMNOE
//ADIDEJFM
//VCEIFGGS

//SGGFIECVAASABCEEJIGOEM
void Test6()
{
    const char* matrix = "ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS";
    const char* str = "SGGFIECVAASABCEEJIGOEM";

    Test("Test6", (const char*) matrix, 5, 8, str, false);
}

//ABCEHJIG
//SFCSLOPQ
//ADEEMNOE
//ADIDEJFM
//VCEIFGGS

//SGGFIECVAASABCEHJIGQEMS
void Test7()
{
    const char* matrix = "ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS";
    const char* str = "SGGFIECVAASABCEHJIGQEMS";

    Test("Test7", (const char*) matrix, 5, 8, str, false);
}

//AAAA
//AAAA
//AAAA

//AAAAAAAAAAAA
void Test8()
{
    const char* matrix = "AAAAAAAAAAAA";
    const char* str = "AAAAAAAAAAAA";

    Test("Test8", (const char*) matrix, 3, 4, str, true);
}

//AAAA
//AAAA
//AAAA

//AAAAAAAAAAAAA
void Test9()
{
    const char* matrix = "AAAAAAAAAAAA";
    const char* str = "AAAAAAAAAAAAA";

    Test("Test9", (const char*) matrix, 3, 4, str, false);
}

//A

//A
void Test10()
{
    const char* matrix = "A";
    const char* str = "A";

    Test("Test10", (const char*) matrix, 1, 1, str, true);
}

//A

//B
void Test11()
{
    const char* matrix = "A";
    const char* str = "B";

    Test("Test11", (const char*) matrix, 1, 1, str, false);
}

void Test12()
{
    Test("Test12", nullptr, 0, 0, nullptr, false);
}

int main(int argc, char* argv[])
{
    Test1();
    Test2();
    Test3();
    Test4();
    Test5();
    Test6();
    Test7();
    Test8();
    Test9();
    Test10();
    Test11();
    Test12();

    return 0;
}

面试题13:机器人的运动范围

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

#include <cstdio>
int movingCountCore(int threshold, int rows, int cols, int row, int col, bool* visited);
bool check(int threshold, int rows, int cols, int row, int col, const bool* visited);
int getDigitSum(int number);

// 这个题要求的是能达到的符合条件的所有格子的数目,有些理解为什么这么写了!
int movingCount(int threshold, int rows, int cols)
{
    if(threshold < 0 || rows <= 0 || cols <= 0)
        return 0;
    bool *visited = new bool[rows * cols];// 建一个矩阵大小的bool矩阵
    // 初始化矩阵
    for (int i = 0; i < rows * cols; ++i) {
        visited[i] = false;
    }

    int count = movingCountCore(threshold, rows, cols, 0, 0, visited);
    delete [] visited;// 防止内存泄漏
    return count;
}

int movingCountCore(int threshold, int rows, int cols, int row, int col, bool* visited)
{
    int count = 0;
    if(check(threshold,rows, cols, row, col, visited))// 判断传进来的(row,col)是否符合约束
    {
        visited[row * cols + col] = true;// 符合的话,相应的bool矩阵置1
        count = 1 + movingCountCore(threshold, rows, cols,
                                    row - 1, col, visited)
            + movingCountCore(threshold, rows, cols,
                              row, col - 1, visited)
            + movingCountCore(threshold, rows, cols,
                              row + 1, col, visited)
            + movingCountCore(threshold, rows, cols,
                              row, col + 1, visited);
    }
    return count;
}
// 判断是否符合约束的函数
bool check(int threshold, int rows, int cols, int row, int col, const bool* visited)
{
    return row >= 0 && row < rows && col >= 0 && col < cols && getDigitSum(row) + getDigitSum(col) <= threshold
        && !visited[row * cols + col];
}
// 求一个输入的整数的各个数位之和
int getDigitSum(int number)
{
    int sum = 0;
    while(number > 0)
    {
        sum += number % 10;
        number /= 10;
    }

    return sum;
}

// ====================测试代码====================
void test(char* testName, int threshold, int rows, int cols, int expected)
{
    if(testName != nullptr)
        printf("%s begins: ", testName);

    if(movingCount(threshold, rows, cols) == expected)
        printf("Passed.\n");
    else
        printf("FAILED.\n");
}

// 方格多行多列
void test1()
{
    test("Test1", 5, 10, 10, 21);
}

// 方格多行多列
void test2()
{
    test("Test2", 15, 20, 20, 359);
}

// 方格只有一行,机器人只能到达部分方格
void test3()
{
    test("Test3", 10, 1, 100, 29);
}

// 方格只有一行,机器人能到达所有方格
void test4()
{
    test("Test4", 10, 1, 10, 10);
}

// 方格只有一列,机器人只能到达部分方格
void test5()
{
    test("Test5", 15, 100, 1, 79);
}

// 方格只有一列,机器人能到达所有方格
void test6()
{
    test("Test6", 15, 10, 1, 10);
}

// 方格只有一行一列
void test7()
{
    test("Test7", 15, 1, 1, 1);
}

// 方格只有一行一列
void test8()
{
    test("Test8", 0, 1, 1, 1);
}

// 机器人不能进入任意一个方格
void test9()
{
    test("Test9", -10, 10, 10, 0);
}

int main(int agrc, char* argv[])
{
    test1();

    test2();
    test3();
    test4();
    test5();
    test6();
    test7();
    test8();
    test9();

    return 0;
}

面试题14:剪绳子

题目:给你一根长度为n 的绳子,请把绳子剪成m段(m和n都是整数,n>1并且m>1),每根绳子的长度记为k[0],k[1],...k[m]。请问k[0]*k[1]*...k[m]可能的最大乘积是多少

#include <iostream>
#include <cmath>
using namespace std;
// ====================动态规划====================
int maxProductAfterCutting_solution1(int length)
{
    if(length < 2)
        return 0;
    if(length == 2)
        return 1;
    if(length == 3)
        return 2;
    // 子问题的最优解存在数组products里。数组中第i个元素表示把长度为i的绳子剪成若干段之后
    // 各段长度乘积的最大值
    int* products = new int[length + 1];
    products[0] = 0;
    products[1] = 1;
    products[2] = 2;
    products[3] = 3;

    int max = 0;
    for (int i = 4; i <= length ; ++i) {
        max = 0;
        for (int j = 1; j <=i/2; ++j) {
            int product = products[j] * products[i-j];
            if(max < product)
                max = product;
            products[i] = max;
        }
    }
    max = products[length];
    delete [] products;

    return max;
}
// ====================贪婪算法====================、

int maxProductAfterCutting_solution2(int length)
{
    if(length < 2)
        return 0;
    if(length == 2)
        return 1;
    if(length == 3)
        return 2;
    // 尽可能多地减去长度为3的绳子段
    int timesOf3 = length / 3;

    // 当绳子最后剩下的长度为4的时候,不能再剪去长度为3的绳子段。
    // 此时更好的方法是把绳子剪成长度为2的两段,因为2*2 > 3*1。
    if(length - timesOf3 * 3 == 1)
        timesOf3 -= 1;

    int timesOf2 = (length - timesOf3 * 3) / 2;

    return (int)(pow(3,timesOf3)) * (int)(pow(2, timesOf2));

}
// ====================测试代码====================
void test(const char* testName, int length, int expected)
{
    int result1 = maxProductAfterCutting_solution1(length);
    if(result1 == expected)
        std::cout << "Solution1 for " << testName << " passed." << std::endl;
    else
        std::cout << "Solution1 for " << testName << " FAILED." << std::endl;
    int result2 = maxProductAfterCutting_solution2(length);
    if(result2 == expected)
        std::cout << "Solution2 for " << testName << " passed." << std::endl;
    else
        std::cout << "Solution2 for " << testName << " FAILED." << std::endl;
}

void test1()
{
    int length = 1;
    int expected = 0;
    test("test1", length, expected);
}

void test2()
{
    int length = 2;
    int expected = 1;
    test("test2", length, expected);
}

void test3()
{
    int length = 3;
    int expected = 2;
    test("test3", length, expected);
}

void test4()
{
    int length = 4;
    int expected = 4;
    test("test4", length, expected);
}

void test5()
{
    int length = 5;
    int expected = 6;
    test("test5", length, expected);
}

void test6()
{
    int length = 6;
    int expected = 9;
    test("test6", length, expected);
}

void test7()
{
    int length = 7;
    int expected = 12;
    test("test7", length, expected);
}

void test8()
{
    int length = 8;
    int expected = 18;
    test("test8", length, expected);
}

void test9()
{
    int length = 9;
    int expected = 27;
    test("test9", length, expected);
}

void test10()
{
    int length = 10;
    int expected = 36;
    test("test10", length, expected);
}

void test11()
{
    int length = 50;
    int expected = 86093442;
    test("test11", length, expected);
}

int main(int agrc, char* argv[])
{
    test1();
    test2();
    test3();
    test4();
    test5();
    test6();
    test7();
    test8();
    test9();
    test10();
    test11();

    return 0;
}

PS:使用动态规划贪婪法解决问题,值得总结和参考学习。

面试题15:二进制中1的个数

题目描述:请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。例如,把9表示成二进制是1001,有2位是1。因此如果输入9 ,则该函数输出2。

可能引起死循环的解法

int NumberOf1(int n)
{
    int count = 0;
    while (n)
    {
        if(n & 1)
            count++;
        n = n >> 1;
    }
    return count;
}

 输入的数n在右移的时候,如果是一个无符号的数值,则用0来补最左边的n位;如果数字原先是一个有符号的数字,则用数字的符号位填补最左边的n位。所以上面的这种写法会导致无限循环。我尝试将函数的形参改为unsigned int  类型之后,上面的函数就可以正常工作了。

常规解法

int NumberOf1(int n)
{
    int count = 0;
    unsigned int flag = 1;
    while (flag)
    {
        if(n & flag)
            count++;
        flag = flag << 1;
    }
    return count;
}

这种解法不是让输入的数n来移动,而是让1来移动,然后做与运算。循环的次数就等于输入的二进制数的位数

高级解法

int NumberOf1(int n)
{
    int count = 0;
    while (n)
    {
        ++count;
        n = (n -1)&n;
    }
    return count;
}

这种解法巧妙的利用位运算的性质,解决了循环次数的问题,提高了循环的效率。嗯,是一种很牛掰的解法。

拓展题

用一条语句来判断输入的数,是不是2的整数次幂,其实就是判断其二进制表示中,是不是只有一个1.

bool JudgeNumIsMultipleOf2(int n)
{
//    if(((n -1)&n) == 0)
//        return true;
//    else
//        return false;
    return ((n - 1) & n) == 0;
}

输入两个整数m和n,那么需要改变m二进制中表示的多少位,才能得到n。先做两个数的异或运算,再对得到的结果做二进制中1的个数的统计。

#include <iostream>
int NumberOf1(int n)
{
    int count = 0;
    while (n)
    {
        ++count;
        n = (n -1)&n;
    }
    return count;
}
int Judge(int n, int m)
{
    int result = m ^ n;
    return NumberOf1(result);
}
int main()
{
    std::cout<<Judge(10,13)<< std::endl;
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值