剑指offer——刷题集

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

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        int len = rotateArray.size();
        int min = rotateArray.at(0);
        if (rotateArray.empty()==1)
        {
            return 0;
        }
        else
        {
            for (int i = 0; i < len; i++)
            {
                if (min>=rotateArray.at(i))
                {
                    min = rotateArray.at(i);
                }
            }
            return min;
        }
    }
};

注意:自己在vs上编译时,调用vector需要加上

#include
using namespace std;

最简洁高效的代码:

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
       sort(rotateArray.begin(),rotateArray.end());//进行排序
        return rotateArray[0];//返回第一个,即最小值
    }
};

2、斐波那契数列的实现
递归方式:

#include
#include
#include
using namespace std;
class Solution {
public:
    int Fibonacci(int n) {
        if (n == 1 || n == 2)
        {
            return 1;
        }
        else
        {
            return Fibonacci(n - 1) + Fibonacci(n - 2);
        }
    }
};
int main()
{
    Solution a ;
    int result = a.Fibonacci(12);
    printf("%d", result);
    return 0;
}

但递归方式消耗的内存会很大,所以考虑非递归方式:

class Solution {
public:
    int Fibonacci(int n) {
        int result = 0,first =1,second =1;
        if (n == 1 || n == 2)
        {
            return 1;
        }
        else
        {
            for (int i = 3; i <= n; i++)
            {
                result = first + second;
                first = second;
                second = result;
            }
            return result;
        }
    }
};

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

#include
#include
#include
using namespace std;
class Solution {
public:
    int jumpFloor(int number) {
        int result = 0, first = 1, second = 2;
        if (number==0)
            return 0;
        else if (number == 1)
            return 1;
        else if (number == 2)
            return 2;
        else
        {
            for (int i = 3; i <= number; i++)
            {
                result = first + second;
                first = second;
                second = result;
            }
            return result;
        }
    }
};
int main()
{
    Solution a ;
    int result = a.jumpFloor(12);
    printf("%d", result);
    return 0;
}

4、变态跳台阶问题

5、求输入整数的二进制数中有多少个1

#include
#include
#include
using namespace std;
class Solution {
public:
    int  NumberOf1(int n) {
        int result = 0;
        unsigned int flag = 1;
        while (flag)
        {
            if (n&flag)//若求有多少个0,则用n|flag
            {
                result++;
            }
            flag=flag << 1;//flag中1的数左移一位,即本来为00000001变为00000010
        }
        return result;
    }
};
int main()
{
    Solution a ;
    int result = a.NumberOf1(24);
    printf("%d", result);
    return 0;
}

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

class Solution {
public:
    double Power(double base, int exponent) {
        double result = 1.0;
        if (exponent==0)
            return 1;
        else if (exponent<0)//也可用abs(exponent)
        {
            int exp = (-1) * exponent;
            for (int j = 0; j < exp; j++)
            {
                result = result*base;
            }
            return 1 / result;
        }
        else
        {
            for (int i = 1; i <= exponent; i++)
            {
                result = result*base;
            }
            return result;
        }
    }
};

考虑需要全面,当输入的次方为正负和0三种情况。
简单快速幂方法:

class Solution {
public:
    double Power(double base, int exponent) {
        long long p = abs((long long)exponent);
      double r = 1.0;
        while(p){
            if(p & 1) r *= base;
            base *= base;
            p >>= 1;
        }
        return exponent < 0 ? 1/ r : r;
    }
};

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

class Solution {
public:
        void reOrderArray(vector<int> &array) {
        int len = array.size();
        vector<int> newarray;
        if (!array.empty())
        {
            for (int i = 0; i < len; i++)
            {
                if (array.at(i)%2)
                {
                    newarray.push_back(array.at(i));//vector容器的函数,表述在vector最后添加一个元素
                }
            }
            for (int i = 0; i < len; i++)
            {
                if (array.at(i)%2==0)
                {
                    newarray.push_back(array.at(i));
                }
            }
        }
        array = newarray;
    }
};

8、把字符串转换为整数
题目描述
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
输入描述:
输入一个字符串,包括数字字母符号,可以为空
输出描述:
如果是合法的数值表达则返回该数字,否则返回0

9、最小的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) {
        int len = input.size();
        vector<int> result;
        //进行排序,不知道为什么vs中vector没有sort函数,就只能手动排序
        for (int i = 0; i < len; i++)
        {
            int min = input.at(i);
            for (int j = i; j < len; j++)
            {
                if (min >= input.at(j))
                {
                    int temp = min;
                    min = input[j];
                    input[i] = min;
                    input[j] = temp;
                }
            }
        }
        if(lenreturn result;
        else
        {
            for (int z = 0; z < k; z++)
            {
                result.push_back(input[z]);
            }
            return result;
        }
    }
};

10、链表的倒序输出
题目描述
输入一个链表,从尾到头打印链表每个节点的值。
思路:用库函数,每次扫描一个节点,将该结点数据存入vector中,如果该节点有下一节点,将下一节点数据直接插入vector最前面,直至遍历完

class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        vector<int> result;
        if (head!=NULL)
        {
            result.insert(result.begin(), head->val);
            while (head->next!=NULL)
            {
                result.insert(result.begin(), head->next->val);
                head = head->next;
            }
        }
        return result;
    }
};

详细掌握vector的库函数:重点有insert();begin();end();push_back()
11、链表的逆序保存
输入一个链表,反转链表后,输出新链表的表头。
0->1->2->3->->5 变成 5->4->3->2->1->00<-1<-2<-3<-4<-5

class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        if (pHead==NULL)
        {
            return NULL;
        }
        ListNode* head = pHead;//新建一个链表
        pHead = pHead->next;
        head->next = NULL;
        while (pHead)
        {
            ListNode* next = pHead->next;
            pHead->next = head;
            head = pHead;
            pHead = next;
        }
        return head;
    }
};

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

class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if (pHead1==NULL&&pHead2!=NULL)
        {
            return pHead2;
        }
        if (pHead1!=NULL&&pHead2==NULL)
        {
            return pHead1;
        }
        if (pHead1==NULL&&pHead2==NULL)
        {
            return NULL;
        }

        ListNode* result = NULL;
        if (pHead1->val<pHead2->val)
        {
            result = pHead1;
            result->next = Merge(pHead1->next, pHead2);
        }
        else
        {
            result = pHead2;
            result->next = Merge(pHead1, pHead2->next);
        }
        return result;
    }
};

13、二叉树的下一个节点
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。(即给定一个二叉树中某一节点,给出改节点的下一个节点)
思路:
剑指offer——刷题集_第1张图片
(1) 若该节点存在右子树:则下一个节点为右子树最左子节点(如图节点 B )
(2) 若该节点不存在右子树:这时分两种情况:
2.1 该节点为父节点的左子节点,则下一个节点为其父节点(如图节点 D )
2.2 该节点为父节点的右子节点,则沿着父节点向上遍历,知道找到一个节点的父节点的左子节点为该节点,则该节点的父节点下一个节点(如图节点 I ,沿着父节点一直向上查找找到 B ( B 为其父节点的左子节点),则 B 的父节点 A 为下一个节点)。

/*
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==NULL)
            return NULL;

        if(pNode->right!=NULL)
        {
            pNode = pNode->right;
            while(pNode->left!=NULL)
            {
                pNode=pNode->left;
            }
            return pNode;
        }

        while(pNode->next!=NULL)
        {
            TreeLinkNode* pRoot = pNode->next;
            if(pRoot->left==pNode)
            {
                return pRoot;
            }
            pNode = pNode->next;
        }
        return NULL;
    }
};

或者更加清晰的逻辑代码为:

public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode) {
        if (pNode == null)
            return pNode;
        if (pNode.right != null) { // 节点有右子树
            pNode = pNode.right;
            while (pNode.left != null) {
                pNode = pNode.left;
            }
            return pNode;
        } else if ( pNode.next != null && pNode.next.left == pNode) { // 节点无右子树且该节点为父节点的左子节点
            return pNode.next;
        } else if (pNode.next != null && pNode.next .right == pNode) { // 节点无右子树且该节点为父节点的右子节点
            while(pNode.next != null && pNode .next .left != pNode){
                pNode = pNode.next ;
            }
            return pNode.next ;
        }else{
            return pNode.next ;//节点无父节点 ,即节点为根节点
        }
    }
}

14、数组中只出现一次的数
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
采用异或的方法,看的小鱼很晕,可以一步步调试看结果帮助理解

class Solution {
public:
    void FindNumsAppearOnce(vector<int> data, int* num1, int *num2) {
        if (data.size() < 2) return;
        int myxor = 0;
        int flag = 1;
        for (int i = 0; i < data.size(); ++i)
            myxor ^= data[i];
        while ((myxor & flag) == 0) flag <<= 1;
        *num1 = myxor;
        *num2 = myxor;
        for (int i = 0; i < data.size(); ++i) {
            if ((flag & data[i]) == 0) *num2 ^= data[i];
            else *num1 ^= data[i];
        }
    }
};

小鱼自己的查找方法,比较笨,但是好算好理解

class Solution {
public:
    void FindNumsAppearOnce(vector<int> data, int* num1, int *num2) {
        int len = data.size();
        vector<int> result;

        for (int i = 0; i < len; i++)
        {
            int count = 0;
            for (int j = 0; j < len; j++)
            {
                if (i!=j)
                {
                    if (data[i]==data[j])
                    {
                        count++;
                    }
                }
            }
            if (count==0)
            {
                result.push_back(data[i]);
            }
        }
        *num1 = result.at(0);
        *num2 = result.at(1);
    }
};

int main()
{
    Solution a;
    vector<int> data = { 12,12,34,34,24,6 };
    int num1, num2;
    a.FindNumsAppearOnce(data, &num1, &num2);
    cout << num1 << "  " << num2;
    return 0;
}

15、从上往下打印二叉树
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
思路:借助队列来完成二叉树的遍历

class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        vector<int> res;
        queue q;
        TreeNode* cur;
        if (root==NULL)
        {
            return res;
        }
        q.push(root);
        while (!q.empty())
        {
            cur = q.front();
            res.push_back(cur->val);
            if (cur->left!=NULL)
            {
                q.push(cur->left);
            }
            if (cur->right != NULL)
            {
                q.push(cur->right);
            }
            q.pop();
        }
        return res;
    }
};

16、二叉搜索树的后序遍历
题目说明:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
思路:二叉搜索树的后序遍历中,最后一个元素为根节点,如果以根节点为基点,可以将数组分成左边全是小与根节点的集合,及数组右边全是大于根节点的集合,则该数组是二叉搜索树的后续遍历结果。

#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        if (sequence.empty() == 1)
        {
            return false;
        }
        int len = sequence.size();
        int root = sequence.at(len - 1);
        int i = 0, count = 0;
        while(root>sequence.at(i))
        {
            i++;
        }
        for (int j = i; j < len - 1; j++)
        {
            if (rootif (count == (len - 1 - i))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
};

int main()
{
    vector<int> a = { 4, 8, 6, 12, 16, 14, 10 };
    Solution res;
    return res.VerifySquenceOfBST(a);
}

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

class Solution {
public:
    int TreeDepth(TreeNode* pRoot)
    {
        if (pRoot==NULL)
        {
            return 0;
        }
        else
        {
            return max(1 + TreeDepth(pRoot->left), 1 + TreeDepth(pRoot->right));
            //利用递归,先处理完1 + TreeDepth(pRoot->left)这个递归
            //下一个循环变成1+(max(1+TreeDepth(pRoot->left),1+TreeDepth(pRoot->left->right))
            //同理就会遍历完所有的情况
        }
    }
};

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

class Solution {
public:
    vector<int> FindNumbersWithSum(vector<int> array, int sum) {
        vector<int> res;
        if (array.empty()==0)
        {
            int len = array.size();
            int start = 0, end = len - 1;
            while (startif (array.at(start)+array.at(end)==sum)
                {
                    res.push_back(array.at(start));
                    res.push_back(array.at(end));
                    break;
                }
                else if (array.at(start) + array.at(end) else
                {
                    end--;
                }
            }
        }
        return res;
    }
};

注意:题目有一个前提,是数组有序,如果没有该前提,记得在代码中先进行排序(快排),再进行查找(首尾交替),这样时间复杂度会下降。
19、实现数组的快速排序及选择排序

//快速排序
void quickSort(int s[], int l, int r)
{
    if (l< r)
    {
        int i = l, j = r, x = s[l];
        while (i < j)
        {
            while (i < j && s[j] >= x) // 从右向左找第一个小于x的数
                j--;
            if (i < j)
            {
                s[i] = s[j];
                i++;
            }
            while (i < j && s[i]< x) // 从左向右找第一个大于等于x的数
                i++;
            if (i < j)
            {
                s[j] = s[i];
                j--;
            }
        }
        s[i] = x;
        quickSort(s, l, i - 1); // 递归调用
        quickSort(s, i + 1, r);
    }
}

//selectsort选排
void selectSort(int* array)
{
    if (array==NULL)
    {
        return;
    }
    int len = sizeof(array);
    int minIndex;
    for (int i = 0; i < len-1; i++)
    {
        minIndex = i;
        for (int j = i+1; j < len-1; j++)
        {
            if (array[minIndex]>array[j])
            {
                minIndex = j;
            }
        }
        if (minIndex!=i)
        {
            swap(array, i, minIndex);
        }
    }

}
void swap(int* array, int i, int minIndex)
{
    int temp = array[i];
    array[i] = array[minIndex];
    array[minIndex] = temp;
}

int main()
{
    int array[] = { 34,65,12,43,67,5,78,10,3,70 }, k;
    int len = sizeof(array) / sizeof(int);
    quickSort(array, 0, len - 1);
    cout << "The sorted arrayare:" << endl;
    for (k = 0; kcout << array[k] << ","<return 0;
}

20、第一次只出现一次的字母
题目描述
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1.
小鱼首先想到的是比较笨的方法,就是遍历查找

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        int len = str.length();
        int i = 0;
        while (iint count = 0;
            int unique = str[i];
            for (int j = 0; j < len; j++)
            {
                if (unique!=str[j])
                {
                    count++;
                }
            }
            if (count==len-1)
            {
                return i;
                break;
            }
            else
            {
                i++;
            }
        }
        return -1;
    }
};

这样的查找比较时间复杂度就是O(n2),看完大牛们的评论才想到更简单的方法:

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        if(str.size()==0)
            return -1;
        char ch[256]={0};//A->65,Z->90,a->97,z->122
        for(int i=0;i<str.size();i++)
            ch[str[i]]++;//这里会将对应的字符转换为ascii码进行存储
        for(int i=0;i<str.size();i++)
            if(ch[str[i]]==1)
                return i;
        return 0;
    }
};

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

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        int len = numbers.size();
        int res = numbers.at(0),count=1;
        for (int i = 1; i < len; i++)
        {
            if (res==numbers.at(i))
            {
                count++;
            }
            else
            {
                count--;
            }
            if (count==0&&i<(len-1))//考虑为奇数的情况,eg:1,2,3,2,4,2,5,2,3
            {
                res = numbers.at(i);
                count = 1;
            }
        }
        if (count>=1)
        {
            return res;
        }
        else
        {
            return 0;
        }
    }
};

小鱼提示,本题也可用上题思路,开创一片内存,存放每个数字出现的次数。
22、栈的压入、弹出序列
题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

#include
#include
using namespace std;
int flag;
class Solution {
public:
    bool IsPopOrder(vector<int> pushV, vector<int> popV) {
        if (((pushV.empty())&&(!popV.empty())) || (popV.size() != pushV.size()))
            return false;
        int len = pushV.size(), a = popV.at(0);
        if (len==1&&pushV.at(0)!=popV.at(0))
        {
            return false;
        }
        //找到出栈的第一个元素在栈中的第几位
        for (int i = 0; i < len; i++)
        {
            if (pushV.at(i)==a)
            {
                flag=i;
            }
        }
        //出栈的第二位开始,若为前一个出栈的后面或前一位都是合理的
        for (int j = 1; j < len; j++)
        {
            for (int k = 0; k < pushV.size(); k++)
            {
                if (pushV.at(k)==popV.at(j))
                {
                    if (k>flag)
                    {
                        pushV.erase(pushV.begin()+flag);//erase操作之后,size就会变
                        flag = k-1;//当下一个出栈为前一个出栈的后边,则flag减一
                    }
                    else if (k==flag-1)
                    {
                        pushV.erase(pushV.begin() + flag);
                        flag = k ;//当下一个出栈为前一个元素前方,flag不变
                    }
                    else
                    {
                        return false;
                    }
                }
            }
        }
        return true;
    }
};

int main() {
    Solution a;
    vector<int> pushV = { 1,2,3,4,5 };
    vector<int> popV = { 4,5,3,2,1 };
    a.IsPopOrder(pushV, popV);
    return 0;
}

这是小鱼傻瓜式分析代码
大牛的代码为

class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        if(pushV.size() == 0) return false;
        vector<int> stack;
        for(int i = 0,j = 0 ;i < pushV.size();){
            stack.push_back(pushV[i++]);
            while(j < popV.size() && stack.back() == popV[j]){
                stack.pop_back();
                j++;
            }      
        }
        return stack.empty();
    }
};

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

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

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

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

用两个栈实现一个队列的功能?要求给出算法和思路!
<分析>:
入队:将元素进栈A
出队:判断栈B是否为空,如果为空,则将栈A中所有元素pop,并push进栈B,栈B出栈;
如果不为空,栈B直接出栈。
用两个队列实现一个栈的功能?要求给出算法和思路!
<分析>:
入栈:将元素进队列A
出栈:判断队列A中元素的个数是否为1,如果等于1,则出队列,否则将队列A中的元素 以此出队列并放入队列B,直到队列A中的元素留下一个,然后队列A出队列,再把 队列B中的元素出队列以此放入队列A中。

小鱼会不断更新本文,有问题可以一起讨论,一起加油

你可能感兴趣的:(c/c++,代码进修)