剑指offer算法题学习

154 阅读15分钟

面试题三、数组中的查找

一个二维数组每行、每列都递增排序。输入一个数字,判断该数字是否在数组中。

/*
例:
1  2  8  9
2  4  9  12
4  7  10 13
6  8  11 15
    
    若输入7则返回true
    若输入5则返回false
*/

bool Find(int *matrix , int rows, int colums, int number)
{
    bool found = false;
    if (matrix != NULL && 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] > numver)
                -- columnn;
            else 
                ++ row;
        }
    }
    return found;
}

面试题五、从尾到头打印链表

// 链表结点定义如下:
struct ListNode
{
    int         m_nKey;
    ListNode*   m_pNext;
};

// 解法一
// 思路:用栈

void PrintListReversingly_Iteratively(ListNode *pHead)
{
    std::stack<ListNode*> nodes;//栈
    
    ListNode* pNode = pHead;
    while (pNode != NULL)
    {
        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 != NULL)
    {
        if (pHead->m_pNext != NULL)
        {
            PrintListReversingly_Recursively(pHead->m_pNext);
        }
        printf("%d\t",pHead->m_nValue);
    }
}

面试题六、重建二叉树

根据前序遍历序列和中序遍历序列重建二叉树

struct BinaryTreeNode
{
    int 			m_nValue;
    BinaryTreeNode* m_pLeft;
    BinaryTreeNode* m_pRight;
};

BinaryTreeNode* Construct(int *preorder, int *inorder,int length)
{
    if (perorder == NULL || inoerder == NULL || length <= 0)
        returen NULL;
    
    return ConstructCore(perorder ,preorder + length - 1,inorder,inorder + length - 1);
}

BinaryTreeNode* ConstructCore(
    int* startPreoder, int* endPreorder, int* startInorder ,int* endInorder)
{
    // 前序遍历序列的第一个数字是根节点的值
    int rootValue = startPreorder[0];
    BinaryTreeNode* root = new BinaryTreeNode();
    root->m_nValue = rootValue;
    root->m_pLeft = root->m_pRight = NULL;
    
    if (startPreorder == endPreorder)
    {
        if (startInorder == endInorder && *startPreorder == *startInorder)
            return root;
        else
            throw std::exception("Invalid input.");
    }
    
    // 在中序遍历中找到根节点的值
    int* rootInorder = startInoder;
    while(rootInorder <= endInorder && *root Inorder != rootValue)
        ++ rootInorder;
    
    if (rootInorder == endInorder && endInorder && *rootInorder != rootValue)
        throw std::exception("Invalid input.");
    
    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;
}

面试题七、用两个栈实现队列

template <typename T> class CQueue
{
public:
    CQueue(void);
    ~CQueue(void);
    
    void appendTail(const T& node);
    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("queue is empty");
    
    T head = stack2.top();
    stack2.pop();
    
    return head;
}

面试题八、旋转数组的最小数字

将某递增数组进行旋转,即{13579}旋转后的结果之一为{79135}
其最小数字为1
{91357}

int Min(int* numbers, int length)
{
    if (numbers == NULL || length <= 0)
        throw new std::exception("Invalid parameters");
    
    int index1 = 0;
    int index2 = length - 1;
    int indexMid = index1;
    
    //当numbers[index1] < numbers[index2] 时,说明该数组没有旋转,还是递增。所以indexMid == index1 == 0,因为index1无论如何也不会大于index2
    while (numbers[index1] >= numbers[index2])
    {
        if (index2 - index1 == 1)
        {
            indexMid = index2;
            break;
        }
        
        indexMid = (index1 + index2) / 2;
        if (numbers[indexMid] >= numbers[index1])
            index1 = indexMid;
        else if (numbers[indexMid] <= numbers[index2])
            index2 = indexMid;
    }
    return numbers[indexMid];
}

面试题九、斐波那契数列

n==0时  f(n)==0;
n==1时  f(n)==1;
n>1时   f(n)==(f(n-1)+f(n-2));

// 递归会很低效,所以最好找出其他办法

//思路 : 先算f(2),再根据f(1)和f(2)算出f(3),然后以此类推算出f(n)
long long Fibonacci(unsigned 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 = fibNMinusOne + fibNMinusTwo;
        
        fibNMinusTwo = fibNMinusOne;
        fibNMinusOne = fibN;
    }
    return fibN;
}

面试题十、二进制中1的个数

int NumberOf1(int n)
{
    int count = 0;
    while (n)
    {
        ++ count;
        n = (n - 1) & n; // 每次这样的操作就会把最右面为1的位变成0,其他位不变
    }
    returen count;
}

	1010100 - 1 = 1010011
    1010100 & 1010011 = 1010000
    
    1010000 - 1 = 1001111    
    1010000 & 1001111 = 1000000
        
    1000000 - 1 = 0111111
    1000000 & 0111111 = 0   

面试题十二、打印1到最大的n位数

void Print1ToMaxOfNDigits(int n)
{
    if (n <= 0)
        return;
    char *number = new char[n + 1];
    memset(number, '0' , n);//将某一块内存中的内容全部设置为指定的值
    number[n] = '\0';
    
    while(!Increment(number))
    {
        PrintNumber(number);
    }
    delete []number;
}

bool Increment(char *number)//判断是否到了n位数
{
    bool isOverflow = false;
    int nTakeOver = 0;
    int nLength = strlen(number);
    
    for (int i = nLength - 1; i >= 0; i --)
    {
        int nSum = number[i] - '0' + nTakeOver;
        if (i == nLength - 1)
            nSum ++;
        
        if (nSum >= 10)
        {
            if (i == 0)
                isOverflow = true;
            else
            {
                nSum -= 10;
                nTakeOver = 1;
                number[i] = '0' + nSum;
            }
        }
        else
        {
            number[i] = '0' + nSum;
            break;
        }
    }
    
    return isOverflow;
}

void PrintNumber(char *number)
{
    bool isBeginning0 = true;
    int nLength = strlen(number);
    
    for (int i = 0; i < nLength; ++ i)
    {
        if (isBeginning0 && number[i] != '0')
            isBeginning0 = false;
        
        if (!isBeginning0)
        {
            printf("%c", number[i]);
        }
    }
    printf("\t");
}

面试题十三、在O(1)时间删除单向链表结点

struct ListNode
{
    int			m_nValue;
    ListNode* 	m_pNext;
};

void DeleteNode(ListNode** pListHead,ListNode* pToBeDeleted)
{
    if(!pListHead || !pToBeDeleted)
        return;
    
    // 要删除的结点不是尾结点
    if (pToBeDeleted->m_pNext != NULL)
    {
        ListNode* pNext = pToBeDeleted->m_pNext;
        pToBeDeleted->m_nValue = pNext->m_nValue;
        pToBeDeleted->m_pNext = pNext->m_pNext;
        
        delete pNext;
        pNext = NULL;
    }
    // 链表只有一个结点,删除头结点(也是尾结点)
    else if (*pListHead == pToBeDeleted)
    {
        delete pToBeDeleted;
        pToBeDeleted = NULL;
        *pListHead = NULL;
    }
    // 链表中有多个结点,删除尾结点
    else 
    {
        ListNode* pNode = *pListHead;
        while(pNode->m_pNext != pToBeDeleted)
        {
            pNode = pNode->m_pNext;
        }
        pNode->m_pNext = NULL;
        delete pToBeDeleted;
        pToBeDeleted = NULL;
    }
}

面试题十四、调整数组顺序使奇数位于偶数前面

void ReorderOddEven(int *pData,unsigned int length)
{
    if (pData == NULL || length == 0)
        return;
    
    int *pBegin = pData;
    int *pEnd = pData + length - 1;
    
    while(pBegin < pEnd)
    {
        // 向后移动pBegin,直到它指向偶数
        while(pBegin < pEnd && (*pBegin & 0x1) != 0)
            pBegin ++;
        
        // 向前移动 pEnd,直到它指向奇数
        while (pBegin < pEnd && (*pEnd & 0x1) == 0)
            pEnd --;
        
        if (pBegin < pEnd)
        {
            int temp = *pBegin;
            *pBegin = *pEnd;
            *pEnd = temp;
        }
    }
}
// 升级版

void Reorder(int *pData, unsigned int length,bool (*func)(int))
{
    if (pData == NULL || length == 0)
        return;
    
    int *pBegin = pData;
    int *pEnd = pData + length - 1;
    
    while (pBegin < pEnd)
    {
        while (pBegin < pEnd && !func(*pBegin))
            pBegin ++;
        while (pBegin < pEnd && func(*pEnd))
            pEnd -- ;
        
        if (pBegin < pEnd)
        {
            int temp = *pBegin;
            *pBegin = *pEnd;
            *pEnd = temp;
        }
    }
}

bool isEven(int n)
{
    return (n & 1) == 0;
}

--------------------------------------
// 函数Reorder把数组pData分成两部分
// 函数isEven是一个具体的标准,即判断一个数是不是偶数,或是不是别的什么数
// 两函数一组合,可以实现各种分离
    
void ReorderOddEven(int *pData, unsigned int length)
{
    Reorder(pData,length,isEven);
}

面试题十五、查找单向链表中倒数第K个结点

struct ListNode
{
    int 	  m_nValue;
    ListNode* m_pNext;
};
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k)
{
    if (pListHead == NULL || k == 0)
        return NULL;
    
    ListNode *pAhead = pListHead;
    ListNode *pBehind = NULL;
    
    for (unsigned int i = 0; i < k - 1; ++ i)
    {
        if (pAhead->m_pNext != NULL)
            pAhead = pAhead->m_pNext;
        else
        {
            return NULL;
        }
    }
    
    pBehind = pListHead;
    while (pAhead->m_pNext != NULL)
    {
        pAhead = pAhead->m_pNext;
        pBehind = pBehind->m_pNext;
    }
    
    return pBehind;
}

面试题十六、反转单链表

struct ListNode
{
    int       m_mKey;
    ListNode* m_pNext;
};
ListNode* ReverseList(ListNode* pHead)
{
    ListNode* pReversedHead = NULL;
    ListNode* pNode = pHead;
    ListNode* pPrev = NULL;
    while (pNode != NULL)
    {
        ListNode* pNext = pNode->m_pNext;
        
        if (pNext == NULL)
            pReversedHead = pNode;
        
        pNode->m_pNext = pPrev;
        
        pPrev = pNode;
        pNode = pNext;
    }
    return pReversedHead;
}

面试题十七、合并两个排序的链表

struct ListNode
{
  	int m_nValue;
  	ListNode* m_pNext;
};
ListNode* Merge(ListNode* pHead1,ListNode* pHead2)
{
    if (pHead1 == NULL)
        return pHead2;
    else if (pHead2 == NULL)
        return pHead1;
    
    ListNode* pMergedHead = NULL;
    
    // 递归
    if (pHead->m_nValue < pHead2->m_nValue)
    {
        pMergedHead = pHead1;
        pMergedHead->m_pNext = Merge(pHead1->m_pNext,pHead2);
    }
    else
    {
        pMergedHead = pHead2;
        pMergedHead->m_pNext = Merge(pHead1,pHead2->m_pNext);
    }
    
    return pMergedHead;
}

面试题十八、树的子结构

题目:输入两棵二叉树A和B,判断B是不是A的子结构

struct BinaryTreeNode
{
    int             m_Valeue;
    BinaryTreeNode* m_pLeft;
    BinaryTreeNode* m_pRight;
};
// 递归判断子节点是否与B的根节点一样
bool HasSubtree (BinaryTreeNode* pRoot1,BinaryTreeNode* pRoot2)
{
    bool result = false;
    
    if (pRoot1 != NULL && pRoot2 != NULL)
    {
        if (pRoot->m_nValue == pRoot2->m_nValue)
            result = DoesTree1HaveTree2(pRoot1,pRoot2);
        if (!result)
            result = HasSubtree(pRoot1->m_pLeft,pRoot2);
        if (!result)
            result = HasSubtree(pRoot1->m_pRight,pRoot2);
    }
    
    return result;
}


// 递归判断
bool DoesTree1HaveTree2(BinaryTreeNode* pRoot1,BinaryTreeNode* pRoot2)
{
    if (pRoot2 == NULL)
        return true;
    
    if (pRoot1 == NULL)
        return false;
    
    if (pRoot1->m_nValue != pRoot2->m_nValue)
        return false;
    
    return DoesTree1HaveTree2(pRoot1->m_pLeft,pRoot2->m_pLeft) && DoesTree1HaveTree2(pRoot1->m_pRight,pRoot2->m_pRight);
}

面试题十九、二叉树的镜像

题目:请完成一个函数,输入一个二叉树,该函数输出它的镜像。(其实就是左右树反转)

struct BinaryTreeNode
{
    int             m_Valeue;
    BinaryTreeNode* m_pLeft;
    BinaryTreeNode* m_pRight;
};
void MirrorRecursively (BinaryTreeNode *pNode)
{
    if ((pNode == NULL) || (pNode->m_pLeft == NULL && pNode->m_pRight))
        return;
    
    BinaryTreeNode *pTemp = pNode->m_pLeft;
    pNode->m_pLeft = pNode->m_pRight;
    pNode->m_pRight = pTemp;
    
    if (pNode->m_pLeft)
        MirrorRecursively(pNode->m_pLeft);
    
    if (pNode->m_pRight)
        MirrorRecursively(pNode->m_pRight);
}

面试题二十、顺时针打印矩阵

1  2  3  4 
5  6  7  8
9  10 11 12
13 14 15 16
print:12348121615141395671110
void PrintMatrixClockwisely(int** numbers,int columns,int rows)
{
    if (numbers == NULL || columns <= 0 || rows <= 0)
        return;
    
    int start = 0;
    
    while (columns > start * 2 && rows > start * 2)
    {
        PrintMatrixInCircle(numbers, columns ,rows ,start);
        ++start;
    }
}

void PrintMatrixInCircle(int** numbers,int columns; int rows ,int start)
{
    int endX = columns - 1 -start;
    int endY = rows - 1 - start;
    
    // 从左到右打印一行
    for (int i = start ; i <= endX; ++i)
    {
        int number = numbers[start][i];
        printNumber(number);
    }
    
    // 从上到下打印一列
    if (start < endY)
    {
        for (int i = start + 1;i <= endY; ++i)
        {
            int numebr = number[i][endX];
            printNumber(number);
        }
    }
    
    // 从右到左打印一行
    if (start < endX && start < endY)
    {
        for (int i = endX - 1;i >= start; --i)
        {
            int number = numbers[endY][i];
            printNumber(number);
        }
    }
    // 从下到上打印一行
    if (start < endX && start > endY - 1)
    {
        for (int i = endY - 1;i >= start + 1; --i)
        {
            int number = numbers[i][start];
            printNumber(number);
        }
    }
}

面试题二十一、包含min函数的栈

template <typename T> void StackWithMin<T>::push(const T& value)
{
    m_data.push(value);
    
    if (m_min.size() == 0 || value < m_min.top())
        m_min.push(value);
    else
        m_min.push(m_min.top());
}

template <typename T> void StackWithMin<T>::pop()
{
    assert(m_data.size() > 0 && m_min.size() > 0);
    
    m_data.pop();
    m_min.pop();
}

template <typename T> const T& StackWithMin<T>::min() const
{
    assert(m_data.size() > 0 && m_min.size() > 0);
    
    return m_min.top();
}

面试题二十二、栈的压入、弹出序列

bool IsPopOrder(const int* pPush,const int* pPop ,int nLength)
{
    bool bPossible = false;
    if (pPush != NULL && pPop != NULL && nLength > 0)
    {
        const int* pNextPush = pPush;
        const int* pNextPop = pPop;
        
        std::stack<int>stackData;
        
        while (pNextPop - pPop < nLength)
        {
            while(stackData.empty() || stackData.top() != *pNextPop)
            {
                if(pNextPush - pPush == nLength)
                    break;
                
                stackData.push(*pNextPush);
                
                pNextPush++;
            }
            
            if (stackData.top() != *pNextPop)
                break;
            
            stackData.pop();
            pNextPop ++;
        }
        
        if (stackData.empty() && pNextPop - pPop == nLength)
            bPossible = true;
    }
    
    return bPossible;
}

面试题二十三、从上往下打印二叉树

struct BinaryTreeNode
{
    int             m_nValue;
    BinaryTreeNode* m_pLeft;
    BinaryTreeNode* m_pRight;
};

// 思路 : 左子树的子树一定在右子树的子树的前面,右子树在左子树的子树的前面

viod PrintFromTopBottom(BinaryTreeNode* pTreeRoot)
{
    if(!pTreeRoot)
        return;
    
    std::deque<BinaryTreeNode *> dequeTreeNode;
    
    dequeTreeNode.push_back(pTreeRoot);
    
    while (dequeTreeNode.size())
    {
        BinaryTreeNode *pNode = dequeTreeNode.front();
        dequeTreeNode.pop_front();
        
        printf("%d ",pNode->m_nValue);
        
        if (pNode->m_pLeft)
            dequeTreeNode.push_back(pNode->m_pLeft);
        
        if (pNode->m_pRight)
            dequeTreeNode.push_back(pNode->m_pRight);
    }
};

面试题二十四、二叉搜索树的后序遍历序列

bool VerifySquenceOfBST(int sequence[],int length)
{
    if (sequence == NULL || length <= 0)
        return false;
    
    int root = sequence[length - 1];
    
    // 在二叉搜索树中左子树的结点小于根结点
    int i = 0;
    for (;i < length - 1; ++ i)
    {
        if (sequence[i] > root)
            break;
    }
    
    // 在二叉搜索树中右子树的结点大于根节点
    int j = i;
    for (; j < length - 1;++ j)
    {
        if (sequence[j] < root)
            return false;
    }
    
    // 判断左子树是不是二叉搜索树
    bool left = true;
    if (i > 0)
        left = VerifySquenceOfBST(sequence, i);
    
    // 判断右子树是不是二叉搜索树
    bool right = true;
    if (i < length - 1)
        right = VerifySquenceOfBST(sequence + i, length - i - 1);
    
    return (left && right);
}

面试题二十五、二叉树中和为某一值的路径

struct BinaryTreeNode
{
    int m_nValue;
    BinaryTreeNode* m_pLeft;
    BinaryTreeNode* m_pRight;
};

void FindPath(BinaryTreeNode* pRoot, int expectedSum)
{
    if (pRoot == NULL)
        return;
    
    std::verctor<int> path;
    int currentSum = 0;
    FindPath(pRoot,expectedSum, path,currentSum);
}

void FindPath(BinaryTreeNode* pRoot, int expectedSum, std::vector<int>& path, int& currentSum)
{
    currentSum += pRoot->m_nValue;
    path.push_back(pRoot->m_nVlaue);
    
    // 如果是叶节点,并且路径上结点的和等于输入的值
    // 打印出这条路径
    bool isLeaf = pRoot->m_pLeft == NULL && pRoot->m_pRight == NULL;
    if (currentSum == expectedSum && isLeaf)
    {
        printf("A path is found: ");
        std::verctor<int >::iterator iter = path.begin();
        for (; iter != path.end(); ++ iter)
            printf("%d\t",iter);
        
        printf("\n");
    }
    
    // 如果是不是叶结点,则遍历它的子节点
    if (pRoot->m_pLeft != NULL)
        FindPath(pRoot->m_pLeft, expectedSum, path, currentSum);
    if (pRoot->m_pRight != NULL)
        FindPath(pRoot->m_pRight, expectedSum, path, currentSum);
    
    // 在返回到父节点之前,在路径上删除当前结点,
    // 并在currentSum中减去当前结点的值
    currentSum -= pRoot->m_nVlaue;
    path.pop_back();
}

面试题二十六、复杂链表的复制

// 第一步:将原结点逐一复制并紧贴原结点之后
void CloneNodes(ComplexListNode* pHead)
{
    ComplexListNode* pNode = pHead;
    while(pNode != NULL)
    {
        ComplexListNode* pCloned = new ComplexListNode();
        pCloned->m_nValue = pNode->m_nValue;
        pCloned->m_pNext = pNode->m_pNext;
        pCloned->m_pSibling = NULL;
        
        pNode->m_pNext = pCloned;
        
        pNode = pCloned->m_pNext;
    }
}

// 第二步:将原结点的第二指向给复制的结点
void ConnectSiblingNodes(ComplexListNode* pHead)
{
    ComplexListNode* pNode = pHead;
    while(pNode != NULL)
    {
        ComplexListNode* pCloned = pNode->m_pNext;
        if(pNode->m_pSibling != NULL)
        {
            pCloned->m_pSibling = pNode->m_pSibling->m_pNext;
        }
        pNode = pCloned->m_pNext;
    }
}

// 第三步:分离
ComplexListNode* ReconnectNodes(ComplexListNode* pHead)
{
    ComplexListNode* pNode = pHead;
    ComplexListNode* pClonedHead = NULL;
    ComplexListNode* pClonedNode = NULL;
    
    if (pNode != NULL)
    {
        pClonedHead = pClonedNode = pNode->m_pNext;
        pNode->m_pNext = pClonedNode->m_pNext;
        pNode = pNode->m_pNext;
    }
    
    while (pNode != NULL)
    {
        pClonedNode->m_pNext = pNode->m_pNext;
        pClonedNode = pClonedNode->m_pNext;
        pNode->m_pNext = pClonedNode->m_pNext;
        pNode = pNode->m_pNext;
    }
    
    return pClonedHead;
}

ComplexListNode* Clone(ComplexListNode* pHead)
{
    CloneNodes(pHead);
    ConnectSiblingNodes(pHead);
    return ReconnectNodes(pHead);
}

面试题二十七、二叉搜索树与双向链表

BinaryTreeNode* Convert(BinaryTreeNode* pRootOfTree)
{
    BinaryTreeNode *pLastNodeInList = NULL;
    ConvertNode(pRootOfTree, &pLastNodeInList);
    
    // pLastNodeInList 指向双向链表的尾结点
    // 我们需要返回头结点
    BinaryTreeNode* pHeadOfList = pLastNodeInList;
    while (pHeadOfList != NULL && pHeadOfList->m_pLeft != NULL)
        pHeadOfList = pHeadOfList->m_pLeft;
    
    return pHeadOfList;
}

void ConvertNode(BinaryTreeNode* pNode,BinaryTreeNode** PLastNodeInlist)
{
    if (pNode == NULL)
        return;
    
    BinaryTreeNode *pCurrent = pNode;
    
    // 递归至最左子结点
    if (pCurrent->m_pLeft != NULL)
        ConvertNode(pCurrent->m_pLeft,pLastNodeInList);
    
    // 最左子节点的父节点的左结点为最左子节点的父节点的左结点
    pCurrent->m_pLeft = *pLastNodeInList; 
    if (*pLastNodeInList != NULL) //*pLastNodeInList 不为空代表该次循环不是最左子节点
        (*pLastNodeInList)->m_pRight = pCurrent; //说明当前结点比上一次递归的值大,应该在上次结点的右边;如果是右子树递归进来的则实际上是重复赋值了
    
    *pLastNodeInList = pCurrent;// 在递归右子树前,将当前结点存入,保证每次递归pLastNodeInList一定比pCurrent小
    
    if (pCurrent->m_pRight != NULL)
        ConvertNode(pCurrent->m_pRight,pLastNodeInList);
    
    // 总结:
    //     pCurrent->m_pLeft = *pLastNodeInList 和 (*pLastNodeInList)->m_pRight = pCurrent 说明pCurrent一定要比*pLastNodeInList大
}

//感悟递归:假设某个方法可以递归,再去尝试写递归的最后一步
//此题感悟:需要借助一个空的参数,即*pLastNodeInList,代表上一次递归的结点,且上一个结点一定要小于当前结点 
// 二叉树的递归:可不可以分别只考虑只有左子树与只有右子树的情况?

面试二十八、字符串的排列

void Permutation(char* pStr)
{
    if (pStr == NULL)
        return;
    
    Permutation(pStr,pStr);
}

void Permutation(char* pStr,char* pBegin)
{
    if (*pBegin == '\0')
    {
        printf("%s\n",pStr);// 下方for循环到最后一个字母的时候打印
    }
    else
    {
        // pCh为当前递归中小循环的字母
        for (char* pCh = pBegin; *pCh != '\0'; ++ pCh)
        {
            char temp = *pCh;// 
            *pCh = *pBegin;// 
            *pBegin = temp; // 
            // 将当前小循环的字母的值 与 将当前小循环的字母的值 互换
            
            Permutation(pStr,pBegin + 1); 
            // 传递正常字符串和原字符串去掉第一个字母的字符串到下一层递归
            // 当pBegin + 1 == '\0' 时,打印当前的pStr
            
             //由于前面交换了一下,所以pCh的内容改变了,我们要还原回来
            temp = *pCh;
            *pCh = *pBegin;
            *pBegin = temp;
        }
    }
}

面试二十九、数组中出现次数超过一半的数字

int MoreThanHalfNum(int* numbers, int length)
{
    if (CheckInvalidArray(numbers,length))
        return 0;
    
    int middle = length >> 1;
    int start = 0;
    int end = length - 1;
    int index = Partition(numbers, length, start, end);
    while (index != middle)
    {
        if (index > middle)
        {
            end = index - 1;
            index = Partition(numbers, length, start, end);
        }
        else
        {
            start = index + 1;
            index = Partition(numbers, length, start, end);
        }
    }
    
    int result = numbers[middle];
    if (!CheckMoreThanHalf(numbers, length, result))
        result = 0;
    
    return result;
}

int Partition(int data[], int length, int start,int end)
{
    if (data == NULL || length <= 0 || start < 0 || end >= length)
        throw new std::exception("Invalid Parameters");
    
    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)
        }
    }
    
    ++ small;
    Swap(&data[small], &data[end]);
    
    return small;
}

bool g_bInputInvalid = false;

bool CheckInvalidArray (int* numbers, int length)
{
    g_bInputInvalid = false;
    if (numbers == NULL && length <= 0)
        g_bInputInvalid = true;
    
    return g_bInputInvalid;
}

bool CheckMoreThanHalf(int* numbers, int length, int number)
{
    int times = 0;
    for(int i =0; i < length; ++ i)
    {
        if (numbers[i] == number)
            times++;
    }
    
    bool isMoreThanHalf = true;
    
    if( times * 2 <= length)
    {
        g_bInputInvalid = true;
        isMoreThanHalf = false;
    }
    
    return isMoreThanHalf;
}
// 第二种解法:
//     如果数组中有一个数字出现的次数超过数组长度的一半,也就是说它出现的次数比其他所有数字出现次数的和还要多。
int MoreThanHalfNum(int* numbers,int length)
{
    if (CheckInvalidArray(numbers, length))
        return 0;
    int result = numvers[0];//保存数组中的一个值
    int times = 1;//记这个值出现的次数
    for (int i = 1; i < length; ++ i)
    {
        if (times == 0) // 当次数为0时
        {
            result = numbers[i];
            times = 1;
        }
        else if (numbers[i] == result)
            times ++;//如果下一个值与之前记录的值相同则次数+1
        else
            times--;//如果下一个值与之前记录的值不同则次数-1
    }
    
    if (!CheckMoreThanHalf(numbers,length,result))
        result = 0;
    
    return result;
}

面试三十、最小的k个数

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

void GetLeastNumbers(int* input , int n ,int* output,int k)
{
    if (input == NULL || output == NULL || k > n || n <= 0 || k <= 0)
        return;
    
    int start = 0;
    int end = n - 1;
    int index = Partition(input , n ,start ,end);
    while(index != k - 1)
    {
        if (index > k - 1)
        {
            end = index - 1;
            index = Partition(input, n , start ,end);
        }
        else
        {
            start = index + 1;
            index = Partition (input , n ,start ,end);
        }
    }
    
    for (int i = 0; i < k)
        output[i] = input[i];
}

如果面试官要求不能修改数组该怎么办?

解法二:O(nlogk)的算法,特别适合处理海量数据

typedef multtiset<int , greater<int> >  intSet;
typedef multtiset<int , greater<int> >::iterator setIterator;

void GetLeastNumbers(const vector<int>& data, intSet& leastNumbers, int k)
{
    leastNumbers.clear();
    
    if (k < 1 || data.size() < k)
        return;
    
    vector<int>::const_iterator iter = data.begin();
    for (; iter != data.end(); ++ iter)
    {
        if ((leastNumbers.size()) < k)
            leastNumbers.insert(*iter);
        else
        {
            setIterator itGreatest = leastNumbers.begin();
            
            if (*iter < *(leastNumber.begin()))
            {
                leasetNumbers.erase(iterGreatest);
                leasetNumbers.insert(*iter);
            }
        }
    }
}

面试题31:连续子数组的最大和

bool g_InvalidInput = false;
int FindGreatestSumOfSubArray(int *pData,int nLength)
{
    if ((pData == NULL) || (nLength <= 0))
    {
        g_InvalidInput = true;
        return 0;
    }
    
    g_InvalidInput = false;
    
    int nCurSum = 0;
    int nGreatestSum = 0x80000000;//负数的最小值
    for (int i = 0;i < nLength; ++i)
    {
        if (nCurSum <= 0)
            nCurSum = pData[i];
        else
            nCurSum += pData[i];
        
        if (nCurSum > nGreatestSum)
            nGreatestSum = nCurSum;
    }
    
    return nGreatestSum;
}

面试题三十二、从1到n整数中1出现的次数

int NumberOf1Between1AndN(int n)
{
    if (n <= 0)
        return 0;
    
    char strN[50];
    sprintf(strN,"%d",n);
    
    return NumberOf1(strN);
}

int NumberOf1(const char* strN)
{
    if (!strN || *strN < '0' || *strN > '9' || *strN == '\0')
        return 0;
    
    int first = *strN - '0';
    unsigned int length = statuc_cast<unsigned int>(strlen(strN));
    
    if(length == 1 && first == 0)
        return 0;
    
    if(length == 1 && first > 0)
        return 1;
    
    // 假设strN是"21345"
    // numFirstDigit是数字10000 ~ 19999的第一个位中的数目
    int numFirstDigit = 0;
    if (first > 1)
        numFirstDigit = PowerBase10(length - 1);
    else if (first == 1)
        numFirstDigit = atoi(strN + 1) + 1;//是把字符串转换成整型数的一个函数
    
    // numFirstDigits 是1346~21345除了第一位之外的数位中的数目
    int numFirstDigits = first * (length - 1) * PowerBase10(length - 2);
    // numRecursive = NumverOf1(strN + 1);
    
    return numFirstDigit + numOtherDigits + numRecursive;
}

int PowerBase10(unsigned int n)
{
    int result = 1;
    for (unsigned int i = 0; i < n; ++ i)
        result *= 10;
    return result;
}

面试题三十三、把数组排成最小的数

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

const int g_MaxNumberLength = 10;

char* g_StrCombine1 = new char[g_MaxNumberLength * 2 + 1];
char* g_StrCombine2 = new char[g_MaxNumberLength * 2 + 1];

void PrintMinNumber(int* numbers, int length)
{
    if (numbers == NULL || length <= 0)
        return;
    char** strNumbers = (char**)(new int[length]);
    for(int i = 0; i < length; ++i)
    {
        strNumbers[i] = new char[g_MaxNumberLength + 1];
        sprintf(strNumbers[i],"%d", numbers[i]);
    }
    
    qsort(strNumbers, length, sizeof (char*),compare);
    
    for (int i = 0;i < length; ++ i)
        printf("%s",strNumbers[i]);
    printf("\n");
    
    for(int i = 0;i < length; ++ i)
        delete[] strNumbers[i];
    delete[] strNumbers;
}
int compare(const void* strNumber1, const void* strNumber2)
{
    strcpy(g_StrCombine1, *(const char**)strNumber1);
    strcpy(g_StrCombine1, *(const char**)strNumber2);
    
    strcpy(g_StrCombine2, *(const char**)strNumber2);
    strcpy(g_StrCombine2, *(const char**)strNumber1);
    
    return strcmp(g_StrCombine1,g_StrCombine2);
}