剑指offer刷题记录

[TOC]

二维数组中的查找

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

解题思路:从右上角的元素进行判断,如果大于target说明这一列肯定都不是,所以将列数见一;如果小于target说明可能在下一行是等于target的,所以这一行都不是解,所以将行数加1.这样通过一个循环进行判断即可。

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        if(!array.empty())
        {
            int row=array.size();
            int col=array[0].size();

            int a=0;
            int b=col-1;

            while(a<row && b>=0)
            {
                if(array[a][b]==target)
                    return true;
                else if(array[a][b]>target)
                {
                    b--;
                }
                else
                    a++;
            }
        }
        return false;

    }
};

替换空格

python一行可以搞定:

# -*- coding:utf-8 -*-
class Solution:
    # s 源字符串
    def replaceSpace(self, s):
        # write code here
        return s.replace(" ","%20")

再回到C++解法上:

本来的想法是用一个新的str2来将所有的字符转好,最后把str2的地址赋值给str的,这样函数查询str其实就是查询已经赋值的str2;但是果然想法还是naive....数组会越界......附上错误代码:

class Solution {
public:
    void replaceSpace(char *str,int length) {
        if(str==NULL)
            return ;
        char *str2;
        //char *str2=(char*)malloc(sizeof(char));
        int j=0;
        string space="%20";
        for(int i=0;str[i]!='\0';i++)
        {
            if(str[i]!=' ')
            {
                str2[j]=str[i];
                j++;
            }
            else
            {
                for(int m=0;m<3;m++)
                {
                    str2[j+m]=space[m];
                }
                j=j+3;
            }
        }
        str=str2;
    }
};

然后在说说正确代码:

class Solution {
public:
    void replaceSpace(char *str,int length) {
        if(str==NULL)
            return ;

        int CountOfBlanks=0; // 空格个数
        int Originallength=0;// 替换前字符个数
        int len=0;           // 替换后字符个数

        for(int i=0;str[i]!='\0';++i)
        {
             Originallength++;
             if(str[i]==' ')
                  ++CountOfBlanks;
        }

        len =Originallength+2*CountOfBlanks;

          // 边界检查:判断字符数组是否越界
        if(len+1>length)
               return ;

        char *ptr1=str+Originallength;
        char *ptr2=str+len;

        while(ptr1!=ptr2)   //字符串收首个连续字母是不会改变内存储对峙的,改变的只是空格后面的存储地址
        {
            if(*ptr1==' ')
            {
               *ptr2--='0';
               *ptr2--='2';
               *ptr2--='%';
            }
            else
            {
                *ptr2--=*ptr1;
            }
        --ptr1;
        }
    }
};

从尾到头打印链表

用stack存,然后再取出来进行打印即可

#include<stack>
#include<vector>
using namespace std;
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        stack<int> res;
        vector<int> s;
        ListNode* node=head;
        while(node!=NULL)
        {
           res.push(node->val);
           node=node->next;
        }

        while(!res.empty())
        {
          s.push_back(res.top());
           res.pop();
        }
        return s;
    }
};

如果要求为“原地算法”,则需要不断的使下一个结点指向前一个结点:

vector<int> printListFromTailToHead2(struct ListNode* head) {
              vector<int> vec;
              ListNode *buf = head;
              ListNode *pre = buf;
              if(head == NULL)
                  return vec;
              while(head->next != NULL){
                  buf = head->next;
                  head->next = buf->next;
                  buf->next = pre;
                  pre = buf;
              }
              while(buf){
                  vec.push_back(buf->val);
                  buf = buf->next;
              }
              return vec;
          }

反转链表

与上题很像,之前在leetcode也刷过:

class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        if(pHead==NULL) return pHead;
        ListNode *dummy=new ListNode(-1);
        dummy->next=pHead;
        ListNode *cur=pHead;
        while(cur->next)
        {
            ListNode *tmp=cur->next;
            cur->next=tmp->next;
            tmp->next=dummy->next;
            dummy->next=tmp;
        }
        return dummy->next;
    }
};

重建二叉树

class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        //首先差错判断
        if(pre.empty() || vin.empty() || pre.size()!=vin.size())
            return NULL;
        int index;
        vector<int> preleft,preright,vinleft,vinright;
        TreeNode *root=new TreeNode(pre[0]);
        for(int i=0;i<vin.size();i++)
        {
            if(vin[i]==pre[0])
            {
                index=i;
                break;
            }
        }

        for(int i=0;i<index;i++)
        {
            preleft.push_back(pre[i+1]);
            vinleft.push_back(vin[i]);
        }

        for(int i=index+1;i<vin.size();i++)
        {
            preright.push_back(pre[i]);
            vinright.push_back(vin[i]);
        }

        root->left=reConstructBinaryTree(preleft,vinleft);
        root->right=reConstructBinaryTree(preright,vinright);
        return root;
    }
};

这段代码的思路比较清晰,通过前序找到根节点,结合中序确定左右子树,然后遍历执行就ok。

用两个栈实现队列

每次push的时候只用一个栈stack1来存,pop的时候先把数据pop到stack2里,然后执行真正的pop操作;之后再将数据已经修改的栈stack2中剩余的数据存回到stack1里。代码如下:

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

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

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

旋转数组的最小数字

sort是比较狗的一个手段,代码如下:

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        if(rotateArray.size()==0) return 0;
        sort(rotateArray.begin(),rotateArray.end());
        return rotateArray[0];
    }
};

如果用sort的话,就不会理解这道题的知识点,这道题的知识点在于利用旋转数组的性质。显然旋转数组可以用二分查找的方法来实现,旋转数组中的第一个数一定是大于最后一个数的,然后要找的最小的数一定是两个递增序列的分界线(此数的左边递增,右边也递增),利用二分查找的思想,设置三个指针分别指向数组的开始(begin),结尾(end),和中间(mid) ,这里附上一个大神的完整代码:

#include<iostream>
using namespace std;
int findOrder(int elements[],int begin,int end)//特殊情况二的顺序查找
{
    int key = elements[begin];
    for (int i = begin; i <= end; ++i)
    {
        if (elements[i] < key)
            return elements[i];
    }
}
int findInSpArray(int elements[], int length)
{
    if (elements == nullptr || length <= 0)//若输入不符合,直接返回
        return 0;
    int begin = 0;
    int end = length - 1;
    int mid = begin;//便于出现特例一时直接返回第一个数
    while (elements[begin] >= elements[end])//每次缩小范围后不停的检测
    {
        if (end - begin == 1){//若范围减小到两个数,即end指向的一定是最小的数
            mid = end;
            break;
        }
        mid = (end + begin) / 2;
        if (elements[begin] == elements[end] && elements[begin] == elements[end])//若出现特例二
        {
            return findOrder(elements, begin, end);
        }
        if (elements[begin] < elements[mid])//最小数在begin之后
            begin = mid;
        else if (elements[end] > elements[mid])//最小数在end之前
            end = mid;
    }
    return elements[mid];//返回最小数
}
int main()
{
    int a[5] = { 3, 4, 5, 1, 2 };
    cout << findInSpArray(a, 5) << endl;
    int b[5] = { 1, 2, 3, 4, 5 };
    cout << findInSpArray(b, 5) << endl;
    int c[5] = { 1, 0, 1, 1, 1 };
    cout << findInSpArray(c, 5) << endl;
    system("pause");
    return 0;
}

斐波拉切数列

递归会报超时,应该是复杂度过大的问题,所以改用非递归。递归的做法如下:

class Solution {
public:
    int Fibonacci(int n) {
        if(n<=0) return 0;
        if(n==1) return 1;
        return Fibonacci(n-1)+Fibonacci(n-2);
    }
};

就写成复杂度为O(n)的代码:

class Solution {
public:
    int Fibonacci(int n) {
        if(n==0) return 0;
        if(n==1 || n==2) return 1;
        int f1=1;
        int f2=1;
        int f;
        for(int i=3;i<=n;i++)
        {
            f=f1+f2;
            f1=f2;
            f2=f;
        }
        return f;
    }
};

跳台阶

明显的递归

class Solution {
public:
    int jumpFloor(int number) {
        if(number==0) return 0;
        if(number==1) return 1;
        if(number==2) return 2;

        return jumpFloor(number-1)+jumpFloor(number-2);
    }
};

变态跳台阶

找规律,发现是\(2^{n-1}\),程序就好写了,见代码:

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

矩形覆盖

找规律,后者是前两者之和,ok

class Solution {
public:
    int rectCover(int number) {
        if(number==0) return 0;
        if(number==1) return 1;
        if(number==2) return 2;

        return rectCover(number-1)+rectCover(number-2);
    }
};

二进制中1的个数

一个小技巧:x&(x-1)可以将整数最右边的1变成0,通过这个小技巧,我们只要循环判断n=n&(n-1)是否为0,即可统计1的个数。整数中有多少个1,则循环多少次。

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

此外还有一种基于位运算的解法:

int count_one_bits(unsigned int n)
{
    n = (n & 0x55555555) + ((n >> 1) & 0x55555555);
    n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
    n = (n & 0x0f0f0f0f) + ((n >> 4) & 0x0f0f0f0f);
    n = (n & 0x00ff00ff) + ((n >> 8) & 0x00ff00ff);
    n = (n & 0x0000ffff) + ((n >> 16) & 0x0000ffff);

    return n;
}

这里实质上类似分治法的思想,0x5就是以1位为单位进行计数,0x3就是以2位为单位进行计数,以此类推:

0xAAAAAAAA=10101010101010101010101010101010
0x55555555 = 1010101010101010101010101010101(奇数位为1,以1位为单位提取奇偶位)
0xCCCCCCCC = 11001100110011001100110011001100
0x33333333 = 110011001100110011001100110011(以“2位”为单位提取奇偶位)
0xF0F0F0F0 = 11110000111100001111000011110000
0x0F0F0F0F = 1111000011110000111100001111(以“8位”为单位提取奇偶位)
0xFFFF0000 =11111111111111110000000000000000
0x0000FFFF = 1111111111111111(以“16位”为单位提取奇偶位)
………………

数值的整次方

考虑到exponent为负数的情况就可以了

class Solution {
public:
    double Power(double base, int exponent) {
        if(exponent==0) return 1;
        int flag=1;
        if(exponent<0)
        {
            flag=0;
            exponent=-exponent;
        }
        double temp=base;
        for(int i=1;i<exponent;i++)
            base=base*temp;
        return flag?base:(double)(1/base);
    }
};

链表中倒数第k个结点

主要注意一下链表会不会越界,思路就是两个指针相距k,一个指针到达尾部的时候另一个指针就自然指向倒数第k个结点了:

class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        if(pListHead==NULL || k<=0) return NULL;
        ListNode *p1,*p2;
        p1=pListHead;
        p2=pListHead;
        for(int i=0;i<k-1;i++)
        {
            if(p2->next!=NULL)
                p2=p2->next;
            else
                return NULL;
        }

        while(p2->next!=NULL)
        {
            p2=p2->next;
            p1=p1->next;
        }

        return p1;

    }
};

合并两个排序的链表

综合判断,如果一个链表已经结束了,则可以直接把这个链表的剩余部分全部添加进新链表里

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

        ListNode *head=new ListNode(0);
        ListNode *l3=head;
        while(pHead1!=NULL && pHead2!=NULL)
        {
            if(pHead1->val<pHead2->val)
            {
                l3->next=pHead1;
                pHead1=pHead1->next;
            }
            else
            {
                l3->next=pHead2;
                pHead2=pHead2->next;
            }
            l3=l3->next;
        }

        while(pHead1!=NULL)
        {
            l3->next=pHead1;
            pHead1=pHead1->next;
            l3=l3->next;
        }

           while(pHead2!=NULL)
        {
            l3->next=pHead2;
            pHead2=pHead2->next;
            l3=l3->next;
        }

        /*直接指向链表的剩余部分,上面的是l3继续扩展逐个复制
        if(pHead1!=NULL)
            l3->next=pHead1;
        if(pHead2!=NULL)
            l3->next=pHead2;
        */
        return head->next;
    }
};

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

比较狗的方法,一个新数组,遍历把奇数先存进来,在遍历一次把偶数存进来。

class Solution {
public:
    void reOrderArray(vector<int> &array) {
        vector<int> res;
        for(int i=0;i<array.size();i++)
        {
            if(array[i]%2!=0)
            {
                res.push_back(array[i]);
            }
        }

         for(int i=0;i<array.size();i++)
        {
            if(array[i]%2==0)
            {
                res.push_back(array[i]);
            }
        }

        array=res;
    }
};

树的子结构

class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot2==NULL || pRoot1==NULL)  //初次判断的时候是检验树的是否是空数,之后的判断就为递归跳出条件了
            return false;
        bool result=false;

        if(pRoot1->val==pRoot2->val)
            result=isPart(pRoot1,pRoot2);

        if(result==false)
            return HasSubtree(pRoot1->left,pRoot2) || HasSubtree(pRoot1->right,pRoot2);
        else
            return result;
    }

    bool isPart(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot2==NULL)
            return true;
        if(pRoot1==NULL)
            return false;
        if(pRoot1->val==pRoot2->val)
            return isPart(pRoot1->left, pRoot2->left) && isPart(pRoot1->right, pRoot2->right);
        else
            return false;
    }

二叉树的镜像

递归方法,将左右子树交换完后,再将左子树的左右子树、右子树的左右子树递归进行交换,代码如下:

class Solution {
public:
    void Mirror(TreeNode *pRoot) {
        if(pRoot==NULL || (pRoot->left==NULL && pRoot->right==NULL))
            return ;

        TreeNode *pTemp=pRoot->left;
        pRoot->left=pRoot->right;
        pRoot->right=pTemp;

        if(pRoot->left)
            Mirror(pRoot->left);

        if(pRoot->right)
            Mirror(pRoot->right);
    }
};

非递归方法:

void MirrorIteratively(TreeNode* pRoot)
{
    if(pRoot == NULL)
        return;

    std::stack<TreeNode*> stackTreeNode;
    stackTreeNode.push(pRoot);

    while(stackTreeNode.size() > 0)
    {
        TreeNode *pNode = stackTreeNode.top();
        stackTreeNode.pop();

        TreeNode *pTemp = pNode->left;
        pNode->left = pNode->right;
        pNode->right = pTemp;

        if(pNode->left)
            stackTreeNode.push(pNode->left);

        if(pNode->right)
            stackTreeNode.push(pNode->right);
    }
}

求\(1+2+……n\)

不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C) ,最初感觉这道题完全无法做;但仔细想一想其实还是有比较多的方法的:

使用递归

这段代码有个小技巧就是bool flag的用法,由于&&判断的依据是如果前者不满足就不会执行后面一句话,所以相当于是一个判断语句if (n>0) then sum+..:

    class Solution
    {
        public:
            int Sum_Solution(int n)
            {
                int sum = n;
                bool flag = (n>0) && ((sum+=Sum_Solution(n-1))>0);
                return sum;
            }
    };  

利用构造函数

class dosum{
public:
    dosum()
    {
        i++;
        sum+=i;
    }

    static void init()
    {
        i=sum=0;
    }

    static int getsum()
    {
        return sum;
    }
private:
    static int i;
    static int sum;
};

int dosum::i=0;
int dosum::sum=0;

class Solution {
public:
    int Sum_Solution(int n) {
        dosum::init();
        dosum *a=new dosum[n];

        delete []a;
        a=NULL;

        return dosum::getsum();

    }
};

这里创建了一个新的class:dosum,在dosum *a=new dosum[n];中相当于将*a指向了一个dosum[n]的数组内存地址上,由于创建一个dosum[n]的数组需要调用n次构造函数,而我们在dosum这个类里定义了相当于循环所需要执行的语句;最后记住要将占用的内存单元清空,否则会有内存泄漏的风险。最后调用函数返回dosum这个类里面的sum,由于是在类里定义的,所以需要一个函数getsum将这个数值传出来。

虚函数

class A;
A* Array[2];  

class A
{
    public:
        virtual int Sum(int n)
        {
            return 0;
        }
};  

class B:public A
{
    public:
        virtual int Sum(int n)
        {
            return Array[!!n]->Sum(n-1)+n;
        }
};  

class Solution
{
    public:
        int Sum_Solution(int n)
        {
            A a;
            B b;
            Array[0] = &a;
            Array[1] = &b;
            return Array[1]->Sum(n);
        }
}; 

不用加减乘除做加法

a是不含进位的和,b是进位,有仅为就重复计算;当b不断左移直到没有1的时候就证明没有仅为了,所以可以放心的把不含仅进位的和a输出来了:

class Solution {
public:
    int Add(int num1, int num2)
    {
        int a;
        int b;

        while(b!=0)
        {
            a=num1^num2;
            b=(num1&num2)<<1;
            num1=a;
            num2=b;
        }

        return a;
    }
};

顺时针打印矩阵

整个程序可以分为两部分,控制边界 与 打印一圈。控制边界就是在每打印一圈后将边界缩小,打印一圈则是将顺时针打印一圈不用考虑第二圈的内容。我们发现圈数与起始点有关。而起点是矩阵的对角线:

class Solution {
public:
    vector<int> printMatrix(vector<vector<int> > matrix) {
        vector<int> ans;
        if(matrix.empty()){
            return ans;
        }

        int rows = matrix.size();
        int columns = matrix[0].size();
        //注意奇数,假设行列为5,则打印中间数时,其位置为(2,2)(从0开始计数的)
        int divRows = (rows + 1) >> 1;
        int divColumns = (columns + 1) >> 1;
        //选择小的考虑
        int startSize = divRows > divColumns?divColumns : divRows;
        for(int start = 0; start < startSize; ++ start){
            //每打印一圈时,行和列都需要递减
            int columnsEnd = columns - 1 - start;
            int rowsEnd = rows - 1 - start;
            //打印当前圈的上面的行
            for(int i = start; i <= columnsEnd; ++ i){
                ans.push_back(matrix[start][i]);
            }

            //打印当前圈的最后列
            for(int i = start + 1; i <= rowsEnd - 1; ++ i){
                ans.push_back(matrix[i][columnsEnd]);
            }

            //打印当前圈的最后行,最后行必须要大于start,否则就可能会重复打印第一行,考虑只有一行的情况
            if(rowsEnd > start){
                for(int i = columnsEnd;i >= start; -- i){
                    ans.push_back(matrix[rowsEnd][i]);
                }
            }

            //打印当前圈的第一列,当前列必须小于columnsEnd,否则可能会重复打印最后列,考虑只有一列的情况
            if(rowsEnd > 1){
                for(int i = rowsEnd - 1; columnsEnd > start && i >= start + 1; -- i){
                    ans.push_back(matrix[i][start]);
                }
            }

        }

        return ans;
    }
};

包含min函数的栈

构造两个栈s1,s2,s1用来存储数据,s2用来存储最小值。

class Solution {
public:
    void push(int value) {
        s1.push(value);
        if(s2.empty() || value<s2.top())
            s2.push(value);
    }
    void pop() {
        if(s1.top()==s2.top())
            s2.pop();
        s1.pop();
    }
    int top() {
        return s1.top();
    }
    int min() {
        return s2.top();
    }
private:
   stack<int>s1,s2;
};

栈的压入、弹出序列

通过栈的弹出序列来模拟弹出过程的代码:

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

从上往下打印二叉树

其实就等价于层次遍历,解法是这样的,用一个队列存着根节点,将这个根节点输出后,再将其左右子树节点弄进队列,将根节点pop出去;重复这个过程直到队列中所有节点已经被输出了(下一轮中左子树节点作为根节点,输出后再将其的左右子树节点添加到队列中来)。由于队列是先进先出的,所以满足层序遍历。

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

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

想要递归的化,光靠题目给的sequence数组可不够,所以return了一个新函数,用它来进行判断。我们写一个后续遍历的记过可以发现,最后一个数字一定是整个树的根节点,然后通过遍历找到前面中第一个大于根节点的数字,他的前一个就是左节点,我们需要判断的就是右子树的节点值是否都大于根节点。再递归判断左子树和右子树就ok了:

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        return BST(sequence,0,sequence.size()-1);

    }
    bool BST(vector<int> seq,int begin,int end)
    {
        if(seq.empty() || begin>end)
            return false;
        int root=seq[end];
        int i=begin;
        for(;i<end;i++)
        {
            if(seq[i]>root)
            {
                break;
            }
        }

        for(int j=i;j<end;j++)
           {
               if(seq[j]<root)
                   return false;
           }

        bool left = true;
        if(i > begin){
            left =BST(seq, begin, i - 1);
        }

        bool right = true;
        if(i < end - 1){
            right = BST(seq, i , end - 1);
        }

        return left && right;
       // return BST(seq,begin,i-1) && BST(seq,i,end-1);
    }
};

数组中的逆序对

暴力解法,超时了:

class Solution {
public:
    int InversePairs(vector<int> data) {
        int count=0;
        for(int i=0;i<data.size()-1;i++)
        {
            for(int j=i+1;j<data.size();j++)
            {
                if(data[i]>data[j])
                    count++;
            }
        }
        return count%1000000007;
    }
};

算法导论第二章的最后一道练习题其实是有这道题的解题思路的,通过修改归并排序来输出逆序对,其实就是分支策略:

class Solution {
public:
    int InversePairs(vector<int> data) {
        if(data.size() == 0){
            return 0;
        }
        // 排序的辅助数组
        vector<int> copy;
        for(int i = 0; i < data.size(); ++i){
            copy.push_back(data[i]);
        }
        return InversePairsCore(data, copy, 0, data.size() - 1) % 1000000007;
    }
    long InversePairsCore(vector<int> &data, vector<int> &copy, int begin, int end){
        // 如果指向相同位置,则没有逆序对。
        if(begin == end){
            copy[begin] = data[end];
            return 0;
        }
        // 求中点
        int mid = (end + begin) >> 1;
        // 使data左半段有序,并返回左半段逆序对的数目
        long leftCount = InversePairsCore(copy, data, begin, mid);
        // 使data右半段有序,并返回右半段逆序对的数目
        long rightCount = InversePairsCore(copy, data, mid + 1, end);

        int i = mid; // i初始化为前半段最后一个数字的下标
        int j = end; // j初始化为后半段最后一个数字的下标
        int indexcopy = end; // 辅助数组复制的数组的最后一个数字的下标
        long count = 0; // 计数,逆序对的个数,注意类型

        while(i >= begin && j >= mid + 1){
            if(data[i] > data[j]){
                copy[indexcopy--] = data[i--];
                count += j - mid;
            }
            else{
                copy[indexcopy--] = data[j--];
            }
        }
        for(;i >= begin; --i){
            copy[indexcopy--] = data[i];
        }
        for(;j >= mid + 1; --j){
            copy[indexcopy--] = data[j];
        }
        return leftCount + rightCount + count;
    }
};

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

这道题开始的时候理解错了,以为在两个链表中找到相同的结点并输出就可以了,后来怎么也通不过....才知道是要找一个公共链表,结构如下图:

    A:a1--->a2
                             c1-->c2-->c3
              /
B:b1-->b2-->b3

代码如下:

class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        // 如果有一个链表为空,则返回结果为空
        if(pHead1 == NULL || pHead2 == NULL){
            return NULL;
        }
        // 获得两个链表的长度
        unsigned int len1 = GetListLength(pHead1);
        unsigned int len2 = GetListLength(pHead2);
        // 默认 pHead1 长, pHead2短,如果不是,再更改
        ListNode* pHeadLong = pHead1;
        ListNode* pHeadShort = pHead2;
        int LengthDif = len1 - len2;
        // 如果 pHead1 比 pHead2 小
        if(len1 < len2){
            ListNode* pHeadLong = pHead2;
            ListNode* pHeadShort = pHead1;
            int LengthDif = len2 - len1;
        }
        // 将长链表的前面部分去掉,使两个链表等长
        for(int i = 0; i < LengthDif; i++){
            pHeadLong = pHeadLong->next;
        }

        while(pHeadLong != NULL && pHeadShort != NULL && pHeadLong != pHeadShort){
            pHeadLong = pHeadLong->next;
            pHeadShort = pHeadShort->next;
        }
        return pHeadLong;
    }
private:
    // 获得链表长度
    unsigned int GetListLength(ListNode* pHead){
        if(pHead == NULL){
            return 0;
        }
        unsigned int length = 1;
        while(pHead->next != NULL){
            pHead = pHead->next;
            length++;
        }
        return length;
    }
};

其中“将长链表的前面部分去掉,使两个链表等长”,这是因为公共链表一定是从某一个节点开始到结尾的节点全部相同才行,所以长链表的前面根本不用考虑。

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

深度优先搜索,用vector<vector<int>> resultvector<int> temp, result来存放最终结果,temp用来存放临时结果。

class Solution {
public:
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
        if(root==NULL)
            return result;

        temp.push_back(root->val);
        if((expectNumber-root->val)==0 && root->left==NULL && root->right==NULL)
            result.push_back(temp);

        FindPath(root->left,expectNumber - root->val);
        FindPath(root->right,expectNumber - root->val);

        temp.pop_back();
        return result;
    }
private:
        vector<vector<int>> result;
        vector<int> temp;
};

孩子们的游戏

约瑟夫问题,通过后一轮的结果可以反推出前一轮的结果,递归解决:

class Solution {
public:
    int LastRemaining_Solution(int n, int m)
    {
        if(n==0) return -1;
        if(n==1) return 0;
        return (LastRemaining_Solution(n-1,m)+m)%n;
    }
};

或者用一个新的数组来进行标识,模拟整个出圈过程:

class Solution {
public:
    int LastRemaining_Solution(int n, int m)
    {
        if(n < 1 || m < 1)  return -1;
        vector<int> vec(n);
        int i = -1,step = 0,count = n;
        while(count > 0){
            i ++;   //指向上一个被删除对象的下一个元素
            if(i >= n) i = 0;   //模拟环
            if(vec[i] == -1) continue;  //跳过被删除对象
            step ++;
            if(step == m){
                vec[i] = -1;
                step = 0;
                count --;
            }
        }
        return i;
    }
};

复杂链表的复制

1:遍历一遍链表,用m_pNext指针将链表连起来的同时,将原链表中的结点N和相应复制结点N‘建立哈希映射

2:再次遍历一遍原链表,对于每一结点m通过哈希找到m‘,并在原链表中找到m的m_pSibling所指向结点,再次通过哈希查找找到m‘的m_pSibling指针应指向的结点,并修改m‘的m_pSibling指针

class Solution {
public:
    typedef std::map<RandomListNode*,RandomListNode*> MAP;
    RandomListNode* CloneNextNode(RandomListNode* pHead,MAP &hashNode)
    {
        RandomListNode* pCopy=new RandomListNode(0);
        RandomListNode* p=pCopy;
        RandomListNode* tmp;
        while(pHead!=NULL)
        {
            tmp=new RandomListNode(pHead->label);
            p->next=tmp;
            hashNode[pHead]=tmp;
            p=p->next;
            pHead=pHead->next;
        }
        return pCopy->next;
    }

    void SetRandomNode(RandomListNode* pHead,RandomListNode* pCopy,MAP &hashNode)
    {
        while(pCopy!=NULL)
        {
            pCopy->random=hashNode[pHead->random];
            pCopy=pCopy->next;
            pHead=pHead->next;
        }
    }

    RandomListNode* Clone(RandomListNode* pHead)
    {
        RandomListNode *pCopy;
        MAP hashNode;
        pCopy=CloneNextNode(pHead,hashNode);
        setRandomNode(pHead,pCopy,hashNode);
        return pCopy;
    }
};

## 二叉搜索树与双向链表

这道题中序排序后就是答案所需要的有序链表,然后只需要将其指针调整为双向链表就可以了。不过感觉这道题的难度真的有点大,在网上看了其他大神的解答表示没有看的非常懂.......

/*
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)
    {
       if(pRootOfTree==NULL)
           return NULL;
        stack<TreeNode*> st;
        st.push(pRootOfTree);

        TreeNode* tmp=pRootOfTree;
        TreeNode* tnp,*res=tmp;
        while(tmp->left!=NULL)
        {
            tmp=tmp->left;
            st.push(tmp);
        }

        if(!st.empty())
        {
            res=st.top();
        }

        while(!st.empty())
        {
            tmp=st.top();
            st.pop();
            tnp=tmp->right;

            if(tnp!=NULL)
            {
              st.push(tnp);
                while(tnp->left!=NULL)
                {
                    tnp=tnp->left;
                    st.push(tnp);
                }
            tnp->left=tmp;
            tmp->right=tnp;
            }
            else{
                if(!st.empty())
                {
                    tmp->right=st.top();
                    tmp->right->left=tmp;
                }
            }
        }
      return res;
    }
};

网上有的递归做法:

class Solution {
public:
    TreeNode* Convert(TreeNode* pRootOfTree)
    {
        if(pRootOfTree == NULL)
            return NULL;
        TreeNode* res = pRootOfTree;
        TreeNode* tmp = res;
        if(tmp->right != NULL) {
            TreeNode* tRight = Convert(tmp->right);
            pRootOfTree->right = tRight;
            tRight->left = pRootOfTree;
        }

        if(tmp->left != NULL) {
            TreeNode* tLeft = Convert(tmp->left);
            res = tLeft;
            while(tLeft->right != NULL) {
                tLeft = tLeft->right;
            }
            tLeft->right = pRootOfTree;
            pRootOfTree->left = tLeft;
        }

        return res;
    }
};

字符串的排列

受到前面有一道题目的影响,第一思路为模拟栈来做,因为栈的入栈与出栈的所有顺序就是字符串的所有排列问题。但是深入分析就发现有问题了,因为自己并不能控制入栈的顺序,或者说确定入栈出栈的顺序其实和原本解救这道题的复杂度是一致的。所以采用一般的思路:

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

利用map就很简单:

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        map<int,int> res;
        int com_len=numbers.size()/2;
        for(int i=0;i<numbers.size();i++)
        {
            res[numbers[i]]++;
            if(res[numbers[i]]>com_len)
            {
                return numbers[i];
            }
        }
        return 0;
    }
};

最小的k个数

题目难度突然下降......前面的几个树做的要死要活的......

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> res;
        if(input.empty() || input.size()<k)
            return res;
        sort(input.begin(),input.end());
        for(int i=0;i<k;i++)
        {
            res.push_back(input[i]);
        }

        return res;
    }
};

字符串的排列

通过遍历,再设置一个temp进行比较就ok了:

class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
        int sum,temp=-65535;
        for(int i=0;i<array.size();i++)
            {
                sum=array[i];
                if(temp <sum)
                    temp=sum;
                for(int j=i+1;j<array.size();j++)
                {
                    sum+=array[j];
                    if(sum>temp)
                        temp=sum;
                }
            }
        return temp;
    }
};

整数中1出现的次数

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        int sum=0;
        for(int i=1;i<=n;i++)
        {
            sum+=countonenumber(i);
        }
        return sum;
    }

private:
    int countonenumber(int i)
    {
        int count=0;
        while(i!=0)
        {
            if(i%10==1)
                count++;
            i=i/10;
        }
        return count;
    }
};

数组中重复的数字

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
    bool duplicate(int numbers[], int length, int* duplication) {
        map<int,int> res;
        for(int i=0;i<length;i++)
        {
            res[numbers[i]]++;
            if(res[numbers[i]]>1)
            {
                *duplication=numbers[i];
                return true;
            }
        }
        return false;
    }
};

字符串的排列

需要注意的是可能会有abac重复字母出现的情况,需要进行判断。

class Solution {
public:
    vector<string> Permutation(string str) {
        if(str.length()==0)
            return res;
        PermutationCore(str,0);
        sort(res.begin(),res.end());
        return res;
    }

    void PermutationCore(string str,int begin)
    {
       if(begin==str.length())
       {
           res.push_back(str);
           return ;
       }
       for(int i=begin;i<str.length();i++)
       {
           if(i!=begin && str[i]==str[begin])
           {
               continue;
           }
           swap(str[begin],str[i]);
           PermutationCore(str,begin+1);
       }
    }
private:
    vector<string> res;
};

把数组排成最小的数

自己最初的想法是计算出numbers中各个元素的位数,最终进行排序。这样想有两个大漏洞,第一个计算的数过大时int类型无法表示(这已经是个大bug了),其次实际上四无法按照我下面代码的思路计算出所有的数的:

class Solution {
public:
    string PrintMinNumber(vector<int> numbers) {
        lengthofnumber(numbers);
        int length=0;
        for(int i=0;i<weishu.size();i++)
        {
            length+=weishu[i];
        }
        getsum(numbers,length);
        sort(sum.begin(),sum.end());

        return sum[0];
    }

    void getsum(vector<int> numbers,int length)
    {
    //计算所有的sum
    }

    void lengthofnumber(vector<int> numbers)
    {
        for(int i=0;i<numbers.size();i++)
        {
            int temp=numbers[i];
            int count=0;
            while(temp!=0)
            {
                count++;
                temp=temp/10;
            }
            weishu.push_back(count);
        }
    }
private:
    vector<int> weishu;
    vector<int> sum;
};

所以用一个新的思路:将字符串进行拼接后进行比较,因为sort可以直接按字母顺序进行排序,拼接后就很简单:

class Solution {
public:
    string PrintMinNumber(vector<int> numbers) {
        int length=numbers.size();
        if(length==0)
            return "";

        sort(numbers.begin(),numbers.end(),cmp);
        string res;
        for(int i=0;i<length;i++)
        {
            res+=to_string(numbers[i]);
        }
        return res;
    }
private:
    static bool cmp(int a,int b)
    {
        string A=to_string(a)+to_string(b);
        string B=to_string(b)+to_string(a);
        return A<B;   //升序排列
    }
};

丑数

放上大神的代码,自己的思路后来验证了有错:

class Solution {
public:
    int GetUglyNumber_Solution(int index) {
        if(index < 7){
            return index;
        }
        vector<int> res(index);
        for(int i = 0; i < 6; i++){
            res[i] = i + 1;
        }
        int t2 = 3, t3 = 2, t5 = 1;
        for(int i = 6; i < index; i++){
            res[i] = min(res[t2] * 2, min(res[t3] * 3, res[t5] * 5));
            while(res[i] >= res[t2] * 2){
                t2++;
            }
            while(res[i] >= res[t3] * 3){
                t3++;
            }
            while(res[i] >= res[t5] * 5){
                t5++;
            }
        }
        return res[index - 1];
    }
};

再贴一份代码:

void GetUgly(int Index)
{
    int i;
    set<int,less<int> > s;
    set<int, less<int> >::iterator It;
    s.insert(1);
    for (i=1 ; i<MAX ; i++)
    {
        if (s.find(i) != s.end() )
        {
            s.insert(2*i) ;
            s.insert(3*i) ;
            s.insert(5*i) ;
        }
    }
    for (It = s.begin() ,i=1 ; It != s.end() && i < Index; It++)
        i++;
    cout << *It << endl;
}
int main(int argc,char *argv[])
{
    int Number;
    cout << "Input a number : " ;
    cin >> Number ;
    GetUgly(Number);
    return 0;
}  

想起来在leetcode上也刷过一道丑数,也放在这里备忘吧:

class Solution {
public:
    bool isUgly(int num) {
        if(num==0) return false;
        if(num<7 && num>0)
            return true;
        while(num%2==0)
            num=num/2;
        while(num%3==0)
            num=num/3;
        while(num%5==0)
            num=num/5;
        if(num==1)
            return true;
        else
            return false;
    }
};

第一次只出现一次的字符

这道题题目说了给定的字符串大小在1到1000之间,结果测试用例里竟然有一个空字符串,无语.......

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        for(int i=0;i<str.length();i++)
        {
            res[str[i]]++;
        }

        for(int i=0;i<str.length();i++)
        {
            if(res[str[i]]==1)
                return i;
        }
        return -1;
    }
private:
    map<char,int> res;
};

在这里扩展一个知识点,关于map的查找:

int main()  

{  

    map<int, string> mapStudent;  

    mapStudent.insert(pair<int, string>(1, "student_one"));  

    mapStudent.insert(pair<int, string>(2, "student_two"));  

    mapStudent.insert(pair<int, string>(3, "student_three"));  

    map<int, string>::iterator iter;  

    iter = mapStudent.find(1);  

    if(iter != mapStudent.end())  

       cout<<"Find, the value is "<<iter->second<<endl;  

    else  

       cout<<"Do not Find"<<endl;  

    return 0;
}  

通过map对象的方法获取的iterator数据类型是一个std::pair对象,包括两个数据 iterator->first和 iterator->second分别代表关键字和存储的数据。

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

然后利用上一题结尾的扩展知识点,我们就可以非常方便的解决这道题:

class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        for(int i=0;i<data.size();i++)
        {
            res[data[i]]++;
        }

        map<int,int>::iterator iter;
        iter=res.find(k);
        if(iter!=res.end())
            return iter->second;
        else
            return 0;
    }
private:
    map<int,int> res;
};

二叉树的深度

这是一道知识点的题,可以利用广度优先和深度优先:

//广度优先
class Solution {
public:
    int TreeDepth(TreeNode* pRoot)
    {
        if(pRoot == NULL){
            return 0;
        }
        queue<TreeNode*> que;
        int depth = 0;
        que.push(pRoot);
        while(!que.empty()){
            int size = que.size();
            depth++;
            for(int i = 0; i < size; i++){
                TreeNode* node = que.front();
                que.pop();
                if(node->left){
                    que.push(node->left);
                }
                if(node->right){
                    que.push(node->right);
                }
            }
        }
        return depth;
    }
};
//深度优先
class Solution {
public:
    int TreeDepth(TreeNode* pRoot)
    {
        if(pRoot == NULL){
            return 0;
        }
        int left = TreeDepth(pRoot->left);
        int right = TreeDepth(pRoot->right);
        return (left > right) ? (left + 1) : (right + 1);
    }
};

平衡二叉树

平衡二叉树指的是左右子树的高度相差不过1:

class Solution {
public:
    bool IsBalanced_Solution(TreeNode* pRoot) {
        if(pRoot == NULL){
            return true;
        }
        int left = TreeDepth(pRoot->left);
        int right = TreeDepth(pRoot->right);
        int diff = left - right;
        if(diff > 1 || diff < -1){
            return false;
        }
        return IsBalanced_Solution(pRoot->right) && IsBalanced_Solution(pRoot->left);
    }
private:
    int TreeDepth(TreeNode* pRoot)
    {
        if(pRoot == NULL){
            return 0;
        }
        int left = TreeDepth(pRoot->left);
        int right = TreeDepth(pRoot->right);
        return (left > right) ? (left + 1) : (right + 1);
    }
};

只遍历一次:

class Solution {
public:
    bool IsBalanced_Solution(TreeNode* pRoot) {
        int depth = 0;
        return IsBalanced(pRoot, &depth);
    }
private:
    int IsBalanced(TreeNode* pRoot, int* depth){
        if(pRoot == NULL){
            *depth = 0;
            return true;
        }
        int left, right;
        if(IsBalanced(pRoot->left, &left) && IsBalanced(pRoot->right, &right)){
            int diff = left - right;
            if(diff <= 1 && diff >= -1){
                *depth = 1 + (left > right ? left : right);
                return true;
            }
        }
        return false;
    }
};

数组中只出现一次的数字

map大法好!

class Solution {
public:
    void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
        for(int i=0;i<data.size();i++)
        {
            res[data[i]]++;
        }
        int flag=1;
        for(int i=0;i<data.size();i++)
        {
            if(res[data[i]]==1)
            {
                if(flag)
                {
                    *num1=data[i];
                    flag=0;
                }
                else
                    *num2=data[i];
            }
        }
    }
private:
    map<int,int> res;
};

和为S的两个数字

不用特意去判断两个数字的乘积大小!由于是递增排序数组,所以可以从首尾判断,小了就将首加1,大了就将尾减1:

class Solution {
public:
    vector<int> FindNumbersWithSum(vector<int> array,int sum) {
        vector<int> result;
        int len=array.size();
        if(len<1)
            return result;

        int pright=len-1;
        int pleft=0;

        while(pright>pleft)
        {
            int curSum=array[pleft]+array[pright];
            if(curSum==sum)
            {
                result.push_back(array[pleft]);
                result.push_back(array[pright]);
                break;
            }
            else if(curSum<sum)
            {
                pleft++;
            }
            else{
                pright--;
            }
        }
        return result;
    }
};

翻转单词顺序列

对每个单词做翻转,然后再整体翻转就可以得到正确的结果:

class Solution {
public:
    string ReverseSentence(string str) {
        string result=str;
        int length=result.size();
        if(length==0)
            return "";

        result+=' ';   //设置一个空格,作为反转标志
        int mark=0;
        for(int i=0;i<length+1;i++)
        {
            if(result[i]==' ')
            {
                Reverse(result,mark,i-1);
                mark=i+1;
            }
        }
        result=result.substr(0,length);
        Reverse(result,0,length-1);
        return result;
    }

private:
    void Reverse(string &str,int begin,int end)
    {
        while(begin<end)
            swap(str[begin++],str[end--]);
    }
};

构建乘积数组

class Solution {
public:
    vector<int> multiply(const vector<int>& A) {
        int res=1;
        for(int i=0;i<A.size();i++)
        {
            res=1;
            for(int j=0;j<A.size();j++)
            {
                if(i==j) continue;
                res=res*A[j];
            }
            b.push_back(res);
        }
        return b;
    }
private:
    vector<int> b;
};

删除链表中重复的结点

以为是将1->2->2->3->3->4->5变成1->2->3->4->5,后来才发现这里指的删除重复结点是变成1->4->5........最初的错误代码:

class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {
        if(pHead==NULL) return NULL;
        ListNode* pre=pHead;
        ListNode* p=pHead;
        while(p->next!=NULL)
        {
            if(p->next->val==p->val)
            {
                if(p==pHead)
                    pHead=p->next->next;
                else{
                 pre->next=p->next;
                 p=p->next;
                }
            }
            else
            {
                pre=p;
                p=p->next;
            }

        }
        return pHead;
    }
};

更正后的代码:

class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {
        if(pHead == NULL){
            return NULL;
        }
        // 指向当前结点前最晚访问过的不重复结点
        ListNode* pPre = NULL;
        // 指向当前处理的结点
        ListNode* pCur = pHead;
        // 指向当前结点后面的结点
        ListNode* pNext = NULL;

        while(pCur != NULL){
            // 如果当前结点与下一个结点相同
            if(pCur->next != NULL && pCur->val == pCur->next->val){
                pNext = pCur->next;
                // 找到不重复的最后一个结点位置
                while(pNext->next != NULL && pNext->next->val == pCur->val){
                    pNext = pNext->next;
                }
                // 如果pCur指向链表中第一个元素,pCur -> ... -> pNext ->...
                // 要删除pCur到pNext, 将指向链表第一个元素的指针pHead指向pNext->next。
                if(pCur == pHead){
                    pHead = pNext->next;
                }
                // 如果pCur不指向链表中第一个元素,pPre -> pCur ->...->pNext ->...
                // 要删除pCur到pNext,即pPre->next = pNext->next
                else{
                    pPre->next = pNext->next;
                }
                // 向前移动
                pCur = pNext->next;
            }
            // 如果当前结点与下一个结点不相同
            else{
                pPre = pCur;
                pCur = pCur->next;
            }
        }
        return pHead;
    }
};

和为S的连续正数序列

class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        for(int i=1;i<sum/2+1;i++)
        {
            int s=i;
            int number=i+1;
            while(s<sum)
            {
                s+=number;
                number++;
            }
            if(s==sum)
            {
                vector<int> temp;
                for(int j=i;j<number;j++)
                    temp.push_back(j);
                result.push_back(temp);
            }
        }
        return result;
    }
private:
    //vector<int> temp;
    vector<vector<int>> result;
};

或者可以设置一个高低指针来做,指针中间的数可以通过公式\(\frac{(plow+phigh)(phigh-plow+1)}{2}\)得出:

class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        vector<vector<int> > result;
        // 高位指针和低位指针
        int phigh = 2, plow = 1;

        // 终止条件是phigh等于sum
        while(phigh > plow){
            // 当前和,使用求和公式s = (a+b) * n / 2
            int curSum = (plow + phigh) * (phigh - plow + 1) >> 1;
            if(curSum < sum){
                phigh++;
            }
            if(curSum == sum){
                vector<int> temp;
                for(int i = plow; i <= phigh; i++){
                    temp.push_back(i);
                }
                result.push_back(temp);
                plow++;
            }
            if(curSum > sum){
                plow++;
            }
        }
        return result;
    }
};

左旋转字符串

思路是逐个往前移,最后一位再重新赋值。记住temp存放的始终是当前字符串的首位,如果是i的话就会发生变动了:

class Solution {
public:
    string LeftRotateString(string str, int n) {
        if(str.empty()) return "";
        int i,j;
        for(i=0;i<n;i++)
        {
            char a=str[0];   //这一步只能是str[0]!!
            for(j=0;j<str.length()-1;j++)
            {
                str[j]=str[j+1];
            }
            str[j]=a;
        }
        return str;
    }
};

扑克牌顺子

这道题其实自己根据几个测试案例修改了代码才通过,勉勉强强算过了吧:

class Solution {
public:
    bool IsContinuous( vector<int> numbers ) {
        if(numbers.size()<5)
            return false;
        sort(numbers.begin(),numbers.end());
        if(numbers[0]<0 || numbers[4]>13)
            return false;
        int flag=0;
        for(int i=0;i<4;i++)
        {
            if(numbers[i]==0)
            {
                flag++;
                continue;
            }
            if((numbers[i+1]-numbers[i])!=1)
                if(flag!=0 && (numbers[i+1]-numbers[i])<=flag+1 &&numbers[i+1]!=numbers[i])
                {
                    flag--;
                    continue;
                }
            else
                return false;
        }
        return true;
    }
};

其中这句(numbers[i+1]-numbers[i])<=flag+1的意思是,前后两个数的距离必须小于等于0(变成任意一个数)的个数,这里的个数用flag进行表示;因为只有这样我们才能用0去填充之间的空白!

正则表达式匹配

只用到了‘.’与‘*‘的匹配规则:

class Solution {
public:
    bool match(char* str, char* pattern)
    {
        if(str==NULL || pattern==NULL)
            return false;
        return matchCore(str,pattern);
    }

private:
    bool matchCore(char *str,char *pattern)
    {
        if(*str=='\0' && *pattern=='\0')
            return true;
        if(*str!='\0' && *pattern=='\0')
            return false;

        if(*(pattern+1)=='*')
        {
            if(*str==*pattern || (*pattern=='.' && *str!='\0'))
            {
                return matchCore(str+1,pattern+2) || matchCore(str+1,pattern) || matchCore(str,pattern+2);
            }
            else
                return matchCore(str,pattern+2);
        }
        if(*str == *pattern || (*pattern == '.' && *str != '\0')){
            return matchCore(str + 1, pattern + 1);
        }
        return false;
    }
};

二叉树的下一个结点

题目中给定的结点不是头结点!!

/*
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;
        TreeLinkNode* pNext=NULL;
        if(pNode->right!=NULL)
        {
            TreeLinkNode* pRight=pNode->right;
            while(pRight->left!=NULL)
                pRight=pRight->left;
            pNext=pRight;
        }
        else if(pNode->next!=NULL)
        {
            TreeLinkNode* pCur=pNode;
            TreeLinkNode* pPar=pNode->next;
            while(pPar!=NULL && pCur==pPar->right)
            {
                pCur=pPar;
                pPar=pCur->next;
            }
            pNext=pPar;
        }
        return pNext;
    }
};

把字符串转换成整数

class Solution {
public:
    int StrToInt(string str) {
        if(str.empty())
            return 0;
        int flag=1;
        int sum=0;
        for(int i=0;i<str.length();i++)
        {
            if(str[i]=='+' && i==0)
            {
                flag=1;
                continue;
            }
            if(str[i]=='-' && i==0)
            {
                flag=-1;
                continue;
            }

            if(str[i]<'0' || str[i]>'9')
                return 0;

            sum=sum*10+(str[i]-'0');
            if(sum>0x7fffffff)
            {
                sum=0;
                break;
            }
        }
        return sum*flag;

    }
};

表示数值的字符串

用正则表达式会不会很简单?

#include<regex>
#include<string>
using namespace std;
class Solution {
public:
    bool isNumeric(char* a)
    {
        if(a==NULL || *a=='\0')
            return false;
        string str;
        while(*a!='\0')
        {
            str+=*a;
            a++;
        }
        regex r("[\\+-]?[0-9]*(\\.[0-9]*)?([eE][\\+-]?[0-9]+)?");

        return regex_match(str,r);
    }

};

常规解法:

public class Solution {
    /**
     * 根据题目要求,正确的输入格式应该符合以下的形式:
     * [sign]integer-digits[.[fragment-digits]][e|E[sign]exponential-digits]
     * []中的表示不一定需要
     * @param str
     * @return
     */
    public boolean isNumeric(char[] str) {
        if (str == null || str.length <= 0) return false;
        int index = 0;
        //判断是否有符号
        if(str[index] == '+' || str[index] == '-'){
            index++;
        }
        //如果只有符号位,则不是正确的数字
        if (index == str.length) return false;
        //扫描数字
        index = scanDigits(str,index);
        if (index < str.length){
            //如果有小数
            if (str[index] == '.'){
                index++;
                index = scanDigits(str,index);
                //如果有指数表示的形式
                if (index < str.length){
                    if (str[index] == 'e' || str[index] == 'E'){
                        index++;
                        return isExponential(str,index);
                    }
                    return false;
                }
                return true;
            }else if (str[index] == 'e' || str[index] == 'E'){
                //如果没有小数,且有指数形式
                index++;
                return isExponential(str,index);
            }
            return false;
        }
        return true;
    }

    private boolean isExponential(char[] str, int index) {
        if (index < str.length){
            //如果是符号,跳到下一个
            if (str[index] == '+' || str[index] == '-'){
                index++;
            }
            index = scanDigits(str,index);
            if (index == str.length) return true;
            return false;
        }
        return false;
    }

    private int scanDigits(char[] str, int index) {
        while (index < str.length && str[index] >= '0' && str[index] <= '9') index++;
        return index;
    }
}

把二叉树打印成多行

这题与下面的这道题是类似的,因为是先进先出的结构所以这道题用两个队列进行存储:

class Solution {
public:
        vector<vector<int> > Print(TreeNode* pRoot) {
            vector<vector<int> > result;
            if(pRoot == NULL){
                return result;
            }
            queue<TreeNode* > nodes[2];
            nodes[0].push(pRoot);
            while(!nodes[0].empty() || !nodes[1].empty()){
                vector<int> v[2];
                while(!nodes[0].empty()){
                    v[0].push_back(nodes[0].front()->val);
                    if(nodes[0].front()->left != NULL){
                        nodes[1].push(nodes[0].front()->left);
                    }
                    if(nodes[0].front()->right != NULL){
                        nodes[1].push(nodes[0].front()->right);
                    }
                    nodes[0].pop();
                }
                if(!v[0].empty()){
                    result.push_back(v[0]);
                }
                while(!nodes[1].empty()){
                    v[1].push_back(nodes[1].front()->val);
                    if(nodes[1].front()->left != NULL){
                        nodes[0].push(nodes[1].front()->left);
                    }
                    if(nodes[1].front()->right != NULL){
                        nodes[0].push(nodes[1].front()->right);
                    }
                    nodes[1].pop();
                }
                if(!v[1].empty()){
                    result.push_back(v[1]);
                }
            }
            return result;
        }
};

按之字形顺序打印二叉树

这道题自己的想法是结合层次遍历与记录层次来实现,就是设置一个队列存放左右子树然后结合是第几层决定了是顺序放还是逆序放进vector,但是后来发现队列来做并不能确定是第几层,所以就换了思路:

使用两个栈。我们在打印某一行结点时,把下一层的子结点保存到相应的栈里。如果当前打印的是奇数层(第一层、第三层等),则先保存左子树结点再保存右子树结点到第一个栈里。如果当前打印的是偶数层(第二层、第四层等),则则先保存右子树结点再保存左子树结点到第二个栈里。

class Solution {
public:
    vector<vector<int> > Print(TreeNode* pRoot) {
        vector<vector<int> > result;
        if(pRoot == NULL){
            return result;
        }
        stack<TreeNode* > s[2];
        s[0].push(pRoot);
        while(!s[0].empty() || !s[1].empty()){
            vector<int> v[2];
            // 偶数行
            while(!s[0].empty()){
                v[0].push_back(s[0].top()->val);
                if(s[0].top()->left != NULL){
                    s[1].push(s[0].top()->left);
                }
                if(s[0].top()->right != NULL){
                    s[1].push(s[0].top()->right);
                }
                s[0].pop();
            }
            if(!v[0].empty()){
                result.push_back(v[0]);
            }
            // 奇数行
            while(!s[1].empty()){
                v[1].push_back(s[1].top()->val);
                if(s[1].top()->right != NULL){
                    s[0].push(s[1].top()->right);
                }
                if(s[1].top()->left != NULL){
                    s[0].push(s[1].top()->left);
                }
                s[1].pop();
            }
            if(!v[1].empty()){
                result.push_back(v[1]);
            }
        }
        return result;
    }
};

链表中环的入口结点

利用快慢指针先找到环内的任何一个节点,然后开始计数从这一个节点到下一次遇到这个节点之间有多少个节点就说明环内有多少个节点。最后通过相隔这么多节点的两个指针开始走整个链表,最后两个指针相遇的地方就是环的入口结点。

class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        if(pHead==NULL)
            return NULL;
        ListNode* meetingnode=MeetingNode(pHead);
        if(meetingnode==NULL)
            return NULL;

        int countloop=1;
        ListNode* p1=meetingnode;
        while(p1->next!=meetingnode)
        {
            p1=p1->next;
            countloop++;
        }
        p1=pHead;
        ListNode* p2=pHead;
        for(int i=0;i<countloop;i++)
        {
            p1=p1->next;
        }
        while(p1!=p2)
        {
            p1=p1->next;
            p2=p2->next;
        }
        return p1;
    }
private:
    ListNode* MeetingNode(ListNode *pHead)
    {
        ListNode *pSlow=pHead->next;
        if(pSlow==NULL)
            return NULL;
        ListNode *pFast=pHead->next->next;
        while(pFast!=NULL && pSlow!=NULL)
        {
            if(pFast==pSlow)
                return pFast;

            pSlow=pSlow->next;
            if(pFast->next!=NULL)
            pFast=pFast->next->next;

        }
       return NULL;
    }
};

字符流中第一个不重复的字符

class Solution
{
public:
  //Insert one char from stringstream
    void Insert(char ch)
    {
        s+=ch;
        res[ch]++;
    }
  //return the first appearence once char in current stringstream
    char FirstAppearingOnce()
    {
        for(int i=0;i<s.length();i++)
        {
            if(res[s[i]]==1)
                return s[i];
        }
        return '#';
    }
private:
    string s;
    map<char,int> res;
};

数据流中的中位数

class Solution {
public:
    void Insert(int num)
    {
        res.push_back(num);
    }

    double GetMedian()
    {
        int len=res.size();
        sort(res.begin(),res.end());
        if(len%2==0)
        {
            double a1=res[len/2-1];
            double a2=res[len/2];
            return (a1+a2)/2.0;
        }
        else
        {
            double a3=res[len/2];
            return a3;
        }
    }
private:
    vector<int> res;
};

序列化二叉树

将树写入一个文件被称为“序列化”,读取文件后重建同样的二叉树被称为“反序列化”。不会做,放上大神的代码:

class Solution {
public:
    char* Serialize(TreeNode *root) {
        if(!root){
            return NULL;
        }
        string str;
        SerializeCore(root, str);
        // 把str流中转换为字符串返回
        int length = str.length();
        char* res = new char[length+1];
        // 把str流中转换为字符串返回
        for(int i = 0; i < length; i++){
            res[i] = str[i];
        }
        res[length] = '\0';
        return res;
    }
    TreeNode* Deserialize(char *str) {
        if(!str){
            return NULL;
        }
        TreeNode* res = DeserializeCore(&str);
        return res;
    }
    void SerializeCore(TreeNode* root, string& str){
        // 如果指针为空,表示左子节点或右子节点为空,则在序列中用#表示
        if(!root){
            str += '#';
            return;
        }
        string tmp = to_string(root->val);
        str += tmp;
        // 加逗号,用于区分每个结点
        str += ',';
        SerializeCore(root->left, str);
        SerializeCore(root->right, str);
    }
    // 递归时改变了str值使其指向后面的序列,因此要声明为char**
    TreeNode* DeserializeCore(char** str){
        // 到达叶节点时,调用两次,都返回null,所以构建完毕,返回父节点的构建
        if(**str == '#'){
            (*str)++;
            return NULL;
        }
        // 因为整数是用字符串表示,一个字符表示一位,先进行转换
        int num = 0;
        while(**str != ',' && **str != '\0'){
            num = num * 10 + ((**str) - '0');
            (*str)++;
        }
        TreeNode* root = new TreeNode(num);
        if(**str == '\0'){
            return root;
        }
        else{
            (*str)++;
        }
        root->left = DeserializeCore(str);
        root->right = DeserializeCore(str);
        return root;
    }
};

二叉搜索树的第k个结点

这道题自己的想法是用一个vector来存储前序遍历的值,同时用一个map存储<TreeNode *,int>,这样在通过vector找到第k个最小的数,然后通过map查找就ok了。。。但是map并不能存放<TreeNode *,int>这样的结构。。。

然后看了大神的思路,发现由于是二叉搜索树,所以可以直接使用中序遍历,这样就能直接得到有序的序列式了:

class Solution {
public:
    TreeNode* KthNode(TreeNode* pRoot, int k)
    {
        if(pRoot == NULL || k == 0){
            return NULL;
        }
        return KthNodeCore(pRoot, k);
    }
private:
    TreeNode* KthNodeCore(TreeNode* pRoot, int &k){
        TreeNode* target = NULL;
        // 先遍历左结点
        if(pRoot->left != NULL){
            target = KthNodeCore(pRoot->left, k);
        }
        // 如果没有找到target,则继续减小k,如果k等于1,说明到了第k大的数
        if(target == NULL){
            if(k == 1){
                target = pRoot;
            }
            k--;
        }
        // 如果没有找到target,继续找右结点
        if(pRoot->right != NULL && target == NULL){
            target = KthNodeCore(pRoot->right, k);
        }
        return target;
    }
};

滑动窗口的最大值

class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size)
    {
        if(size==0)
            return res;
        for(int i=0;i<num.size()-size+1;i++)
        {
            int max=-65535;
            vector<int> temp;
            for(int j=i;j<i+size;j++)
            {
                if(max<num[j])
                    max=num[j];
                temp.push_back(num[j]);
            }
            res.push_back(max);
        }
        return res;
    }
private:
    vector<int> res;
};

机器人的运动范围

开始想用遍历,发现复杂度过大:

class Solution {
public:
    int movingCount(int threshold, int rows, int cols)
    {
        int count=0;
        for(int i=0;i<rows;i++)
        {
            for(int j=0;j<cols;j++)
            {
                int sum=0;
                while(i!=0)
                {
                    sum+=i%10;
                    i=i/10;
                }
                while(j!=0)
                {
                    sum+=j%10;
                    j=j/10;
                }

                if(sum<threshold)
                    count++;
            }
        }
        return count;
    }
};

大神代码如下:

class Solution {
public:
    int movingCount(int threshold, int rows, int cols)
    {
        int count = 0;
        if(threshold < 1 || rows < 1 || cols < 1){
            return count;
        }
        bool* visited = new bool[rows*cols];
        memset(visited, 0, rows*cols);
        count = movingCountCore(threshold, rows, cols, 0, 0, visited);
        delete[] visited;
        return count;
    }
private:
    int movingCountCore(int threshold, int rows, int cols, int row, int col, bool* visited){
        int count = 0;
        if(row >= 0 && row < rows && col >= 0 && col < cols && getDigitSum(row)+getDigitSum(col) <= threshold && !visited[row*cols+col]){
            visited[row*cols+col] = true;
            count = 1 + movingCountCore(threshold, rows, cols, row+1, col, visited)
                + movingCountCore(threshold, rows, cols, row-1, col, visited)
                + movingCountCore(threshold, rows, cols, row, col+1, visited)
                + movingCountCore(threshold, rows, cols, row, col-1, visited);
        }
        return count;
    }
    int getDigitSum(int num){
        int sum = 0;
        while(num){
            sum += num % 10;
            num /= 10;
        }
        return sum;
    }
};

原文地址:https://www.cnblogs.com/yunlambert/p/9175073.html

时间: 2024-09-30 16:26:04

剑指offer刷题记录的相关文章

剑指offer刷题—二维数组的查找

最近接触到一本书叫做剑指offer,在这里准备在这个2个月左右将这本书刷完,当然,不需要每天多少道什么的,不在多,一天理解一道就好了,希望能成为一种习惯,另外,准备在github上也进行同步分享. 今天第一道题: 面试题3:二位数组中的查找 当我们需要解决一个复杂问题时,一个很有效的方法就是从具体的问题出手,通过分析具体的例子,得到规律. 再一个二维数组中,每一行都要按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排列.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含

剑指offer刷题第四题

第四题 重建二叉树 思路:通过前序序列确定根节点,在中序序列找到其位置,确定左右子树,并把对应左右子树的数组根据根节点位置拷贝到新数组中,递归调用得到左右子树. 时间复杂度:O(n). 代码: /** * Definition for binary tree * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ public cla

剑指offer刷题

  题目描述: 输入一个链表,按链表从尾到头的顺序返回一个ArrayList. 思路: 这里用了一个数组reverse,但是实际可以用递归 # -*- coding:utf-8 -*- # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: # 返回从尾部到头部的列表值序列,例如[1,2,3] def printListFromTailToHead(self, lis

剑指offer 刷题 01

题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 链接:https://www.nowcoder.com/questionTerminal/abc3fe2ce8e146608e868a70efebf62e来源:牛客网 两种思路 一种是: 把每一行看成有序递增的数组, 利用二分查找, 通过遍历每一行得到答案, 时间复杂度是nlogn public

剑指offer刷题01

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序. 请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. class Solution { public: bool Find(int target, vector<vector<int> > array) { int size_1=array.size(); //行数 int size_2=array[0].size(); //列数 if (!

剑指offer链表题的双指针法总结

本篇博客旨在总结双指针法在剑指offer链表题中的应用 包括删除链表中重复的节点.链表中倒数第k个节点.链表中环的入口节点.反转链表.合并两个排序的链表.两个链表的第一个公共节点. 根据双指针的类型,可以大致分为三种: 第一种是间隔一定距离的双指针法,包括删除链表中重复的节点.链表中倒数第k个节点两题 删除链表中重复的节点 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3->4->4->5

剑指 offer 第一题: 二维数组中的查找

打算写 图解剑指 offer 66 题 的系列文章,不知道大家有没有兴趣 ?? 题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 题目分析 图 1 如果没有头绪的话,很显然使用 暴力解法 是完全可以解决该问题的. 即遍历二维数组中的每一个元素,时间复杂度:O(n^2). 其实到这里我们就可以发现,使用这种暴力解法并没有充分利用题目给出的信息.这

剑指offer编程题Java实现——面试题12相关题大数的加法、减法、乘法问题的实现

用字符串或者数组表示大数是一种很简单有效的表示方式.在打印1到最大的n为数的问题上采用的是使用数组表示大数的方式.在相关题实现任意两个整数的加法.减法.乘法的实现中,采用字符串对大数进行表示,不过在具体的计算中,还是要将字符串转化成字符数组来进行计算. 实现两个大数的加法,要考虑到两个问题,两个数的和的位数问题,以及如何处理两个数按位相加产生的进位问题.首先两个整数相加,两个数的和的位数最多比最大的整数的位数多1:这样和的位数就确定了.对于进位问题,我的做法是先进行按位相加,相加操作完成后再按照

剑指offer编程题Java实现——面试题9斐波那契数列

题目:写一个函数,输入n,求斐波那契数列的第n项. 1 package Solution; 2 3 /** 4 * 剑指offer面试题9:斐波那契数列 5 * 题目:写一个函数,输入n,求斐波那契数列的第n项. 6 * 0, n=1 7 * 斐波那契数列定义如下:f(n)= 1, n=2 8 * f(n-1)+f(n-2), n>2 9 * @author GL 10 * 11 */ 12 public class No9Fibonacci { 13 14 public static void