剑指offer

本文精选《剑指Offer》中的经典算法题目,涵盖数组、链表、二叉树、字符串等数据结构,深入解析每道题目的解题思路与代码实现,旨在帮助读者提升算法理解和编程实践能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

剑指offer

持续更新…

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

方法一:循环遍历查找,依次遍历二维数组直至查找到目标整数

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        int row = array.size(), col = array[0].size();
        for(int i=0; i<row; i++)
            for(int j=0; j<col; j++){
                if(target == array[i][j])
                    return true;
            }
        return false;
    }
};

方法二:二分查找,根据排列大小顺序查找,从左下角或者右上角开始遍历查找

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        int row=array.size();int column=array[0].size();
        int i=0,j=column-1;
        while(i<row&&j>=0){
            if( array[i][j]==target) return true;
            else if(array[i][j]>target) j--;
            else i++;
             
        }
       return false;
    }
};
Q2: 请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

解题思路:先计算出替换后字符串的长度,然后从字符串末端开始构造新字符串,避免原字符串被覆盖

class Solution {
public:
    void replaceSpace(char *str,int length) {
        if(str == NULL||length<=0)
            return;
        int len = 0;
        int count = 0;
        int i=0;
        while(str[i]!='\0')
        {
            if(str[i]==' ')
                count++;
            len++;
            i++;
        }
        int newlen=len+count*2;
        if(length<newlen)
            return;
        int a=len;
        int b=newlen;
        while(a>=0&&b>=0)
        {
            if(str[a]==' ')
            {
                str[b--]='0';
                str[b--]='2';
                str[b--]='%';
            }
            else{
                str[b]=str[a];
                b--;
            }
            a--;
        }
        return;
    }
};

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

思路一:利用vector容器insert方法,每次插到头部

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution{
public:
	vector<int> printListFromTailToHead(ListNode* head){
        vector<int> result;
        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;
	}
}

思路二:利用堆栈先进后出的性质先把链表数据全部push到一个stack中,然后将stack中的数据pop到一个vector中

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution{
public:
	vector<int> printListFromTailToHead(ListNode* head){
      vector <int>  result;
      stack<int> arr;
      ListNode* p=head;
      while(p!=NULL)
      {
       arr.push(p->val);
       p=p->next;
      }
     int len=arr.size();
     for(int i=0;i<len;i++)
      {
       result.push_back(arr.top());  
       arr.pop();
     }
           return  result;
    }
}
Q4: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

思路一:python实现,已知前序遍历顺序是从根到叶,且根在左子树之前,左子树在右子树之前;中序遍历是从左叶到根再到右叶,即左子树在根之前,根在右子树之前;后序遍历是从叶到根,且左子树在右子树之前,右子树在根之前。所以通过前序遍历的顺序可以得到根节点,再通过根节点将中序遍历分隔左子树和右子树并得到左右叶结点。

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

def reConstructBinaryTree(pre, tin):
    if not pre or not tin:
        return None
    root = TreeNode(pre.pop(0))
#     print(root.val)
#     print(pre)
#     print("######"*5)
    index = tin.index(root.val)
    root.left = reConstructBinaryTree(pre, tin[:index])
#     print("------"*5)
    root.right = reConstructBinaryTree(pre, tin[index + 1:])
#     print("******"*5)
    return root

思路二:用C++实现,具体思路和python代码类似

/**
     * Definition for binary tree
     * struct TreeNode {
     *     int val;
     *     TreeNode *left;
     *     TreeNode *right;
     *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
     * };
     */
  
    class Solution {
    public:
        struct TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> in) {
            int inlen=in.size();
            if(inlen==0)
                return NULL;
            vector<int> left_pre,right_pre,left_in,right_in;
            //创建根节点,根节点肯定是前序遍历的第一个数 
            TreeNode* root=new TreeNode(pre[0])  
            //找到中序遍历根节点所在位置,存放于变量gen中  
            int gen=0; 
            for(int i=0;i<inlen;i++){
                if (in[i]==pre[0]){
                    gen=i;
                      break;
                }
            } 
            //对于中序遍历,根节点左边的节点位于二叉树的左边,根节点右边的节点位于二叉树的右边
            //利用上述这点,对二叉树节点进行归并
            for(int i=0;i<gen;i++){
                left_in.push_back(in[i]);
                left_pre.push_back(pre[i+1]);//前序第一个为根节点
            }
            for(int i=gen+1;i<inlen;i++){
                right_in.push_back(in[i]);
                right_pre.push_back(pre[i]);
            }
            //和shell排序的思想类似,取出前序和中序遍历根节点左边和右边的子树
            //递归,再对其进行上述所有步骤,即再区分子树的左、右子子数,直到叶节点
           root->left=reConstructBinaryTree(left_pre,left_in);
           root->right=reConstructBinaryTree(right_pre,right_in);

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

思路一:一个vector存放数据,一个vector存放入栈顺序号

class Solution{
public:
    int cout = 0;
    void push(int node){
        //将node push到stack1的最后一位
        stack1.push_back(node);
        stack2.push_back(cout++);
    }
    int pop(){
        int i = 0;
        while(stack2[i] == -1)
            i++;
        stack2[i] = -1;
        return stack1[i];
    }
    private:
    vector<int> stack1;//数据
    vector<int> stack2;//顺序
};

思路二:先将数据都压入stack1,然后将stack1中的数据push到stack2,最后pop stack2

class Solution{
public:
    int cout = 0;
    void push(int node){
        stack1.push(node);
    }
    int pop(){
        int a;
        if(stack2.empty())
            while(!stack1.empty()){
            	// 将stack1中的数据push到stack2中
                a = stack1.top();
                stack2.push(a);
                stack1.pop();
            }
        // 将stack2中的数据pop出
        a = stack2.top();
        stack2.pop();
        return a;
    }
    private:
    stack<int> stack1;
    stack<int> stack2;
};
Q6: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{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[0];
        if(len==0)
            return 0;
        else{
            for(int i=1; i<len; i++){
                if(min<rotateArray[i])
                    min = min;
                else
                	min = rotateArray[i];
            }
            return min;
        }
    }
};

思路二:先对数组按照从大到小排序,然后输出第一个值

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        sort(rotateArray.begin(),rotateArray.end());
      
        return rotateArray[0];
    }
};
Q7: 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。

思路一: 传统解法,递归实现

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

思路二:循环实现

class Solution {
public:
    int Fibonacci(int n) {
        int f = 0, g = 1;
        while(n--) {
            g += f;
            f = g - f;
        }
        return f;
    }
};
Q8: 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

思路:很神奇的是,这题本质也是一个斐波那契数列。通过列举可以发现f(1)=1; f(2)=2; f(3)=3; f(4)=5; f(5)=8…不用往下列举可以发现这是斐波拉契级数,但是Q7中不同的是,Q8的n是从1开始而不是0.

分析该题可以发现:青蛙跳跃有两个步长0和1,因此对于第n级台阶来说可以分解为两种不同方法完成,一是先到第n-1个台阶,然后跳一步到第n个台阶;二是先到第n-2个台阶,然后跳两步到第n个台阶,因此可以得到f(n)=f(n-1)+f(n-2).

class Solution {
public:
    int jumpFloor(int number) {
        int f = 1, g = 2;
        while(--number) {
            g += f;
            f = g - f;
        }
        return f; 
    }
};
Q9:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

思路1:该题可以分解为多少步跳上n级台阶;由于每次跳的步数不定,所以可以看作排列组合问题,即将n随机分为m块,由简单的插空法可以解决。因此用m步跳上n级台阶有Cn−1mC_{n-1}^mCn1m种分法,而由组合数求和公式可以知道∑m=0m=nCnm=2n\sum_{m=0}^{m=n}C_{n}^m=2^{n}m=0m=nCnm=2n,因此青蛙跳上n级台阶有2n−12^{n-1}2n1种方法。

思路2:思路1中的分析不算通俗解法,通俗解法应该如Q8中的思路,类似Q8中的思路,对应Q9有:
f(1)=1; f(1)=1; f(1)=1;

f(2)=1+f(1)=2f(1)=2; f(2)=1+f(1)=2f(1)=2; f(2)=1+f(1)=2f(1)=2;

f(3)=1+f(1)+f(2)=2f(2)=4 f(3)=1+f(1)+f(2)=2f(2)=4 f(3)=1+f(1)+f(2)=2f(2)=4

...... ...... ......

f(n)=1+f(1)+f(2)+f(3)+...+f(n−2)+f(n−1)=2f(n−1)=2n−1 f(n)=1+f(1)+f(2)+f(3)+...+f(n-2)+f(n-1)=2f(n-1)=2^{n-1} f(n)=1+f(1)+f(2)+f(3)+...+f(n2)+f(n1)=2f(n1)=2n1

class Solution {
public:
    int jumpFloorII(int number) {
        int n=1;
        while(--number)
            n *= 2;
        return n;
    }
};
Q10:我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

思路:该题依旧是一个斐波拉契数列,分析方式如Q8

class Solution {
public:
    int rectCover(int number) {
        if(number == 0)
            return 0;
        if(number == 1)
            return 1;
        if(number == 2)
            return 2;
          
        int array[number+1];
          
        array[0]=0;
        array[1]=1;
        array[2]=2;
          
        for(int i=3;i<number+1;i++){
            array[i] = array[i-1]+array[i-2];
        }
          
        return array[number];
    }
};
Q11:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

思路1:数字在计算机中本身是由二进制存储的,所以做位运算可以很方便的得到二进制中1的个数

class Solution {
public:
     int  NumberOf1(int n) {
         int count=0;
         unsigned int flag=1;
         while(flag){
             if (n & flag){
                 count++;
             }
          // flag依次变为1,10,100,1000,...(二进制表示)
          flag=flag<<1;
         }
         return count;
     }
};

思路2:这是一个相当巧妙的技巧,不知道大佬是怎么想到的,如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。 所以每次将n与n-1做&运算可以消去一个1.

public class Solution {
    public int NumberOf1(int n) {
        int count = 0;
        while(n!= 0){
            count++;
            n = n & (n - 1);
         }
        return count;
    }
}
Q12: 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

常规解法

class Solution {
public:
    double Power(double base, int exponent) {
        double n = base;
        if(exponent==0)
            return 1;
        if(exponent>0){
            while(--exponent)
            base *= n;
        }
        if(exponent<0){
            exponent = -exponent;
            while(--exponent)
            base /= n;
            if(base==0)
                return 0;
            base = 1/base;
        }
        return base;
    }
};

用位运算来计算次数,不要对正负数分开讨论

class Solution {
public:
    double Power(double base, int exponent) {
        long long p = abs((long long)exponent);
      double r = 1.0;
        while(p){
            // 如果位数是1乘两次,位数是0乘一次
            if(p & 1) r *= base;
            base *= base;
            p >>= 1;
        }
        return exponent < 0 ? 1/ r : r;
    }
};

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

思路1:奇数插到前面,偶数插到后面

class Solution {
public:
    void reOrderArray(vector<int> &array) {
        vector<int> s;
        int n = array.size();
        int k = 0;
        for(int i=0; i<n; i++){
            if(array[i]%2==1){
            // insert(s.begin()+k, array[i]表示将array[i]插到s的第k个数之前)
                s.insert(s.begin()+k,array[i]);
                k++;
            }
            if(array[i]%2==0){
            // s.push_back(a)表示将a插到s最后一位
                s.push_back(array[i]);
            }
        array=s;
        }
    }
};

思路二:先遍历找出奇数,依次插到最后,然后遍历找出偶数,依次插到最后

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

思路三:依次插入,找出偶数插入到最后并擦除原位置的数据

class Solution {
public:
    void reOrderArray(vector<int> &array) {
        vector<int> b;
        for(int i = 0; i < array.size();){
            if(array[i]%2==0){
                b.push_back(array[i]);
                array.erase(array.begin()+i);
            }else{
                i++;
            }
        }
        array.insert(array.end(),b.begin(),b.end());
    }
};
Q14: 输入一个链表,输出该链表中倒数第k个结点。

思路:定义两个指向链表头的指针p1,p2,p1走k-1步后p2开始走,当p1到达链表尾时,p2指向倒数第k个结点

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
        ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        if (pListHead==NULL || k==0) return NULL;
        ListNode* kth=NULL,*end=pListHead;
        int count=1;
        while(end!=NULL){
            if(count++==k){
                kth=pListHead;
            }else if(count>k){
                kth=kth->next;
            }
            end=end->next;
        }
        return kth;
    }
};

另:很笨的一个方法是先统计出链表中有多少个结点(假设n个),然后再指向n-k结点也就是倒数第k个结点,但是我的程序没有AC,找不到原因,先贴这,希望有大佬可以指出【表情】在这里插入图片描述

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
        ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        if (pListHead==NULL) return pListHead;
        if(k==0) return NULL;
        ListNode* kth=pListHead,*end=pListHead;
        int count=1;
        while(end!=NULL){
            count++;
            end=end->next;
        }
        int t = count - k;
        while(--t){
            kth=kth->next;
        }
        return kth;
    }
};

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

思路1:先讲链表的值依次压入堆栈,然后pop到新链表并输出

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        if(pHead == NULL || pHead->next == NULL) return pHead;
        stack<ListNode* > s;
        while(pHead -> next){
            s.push(pHead);
            pHead = pHead -> next;
        }
        ListNode* head = pHead;
        while(!s.empty()){
            pHead -> next = s.top();
            pHead = pHead -> next;
            s.pop();
        }
        p -> next = NULL;
        return head;
    }
};

思路2:直接反转链表,先保存head的下一个结点到next,然后让head指向pre,再让pre挪到head,head挪到next,举个例子可以让操作更直观:(1)a->b->c->d; (2)a<-b c->d; (3)a<-b<-c d; (4)a<-b<-c<-d

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        if(pHead == NULL || pHead->next == NULL) return pHead;
        ListNode* pre=NULL;
        ListNode* next=NULL;
        while(pHead != NULL){
            next = pHead->next;
            pHead->next = pre;
            pre = pHead;
            pHead = next;
        }
       
        return pre;
    }
};
Q16:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则

思路1: 递归

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

思路2:依次比较两个链表中的结点并将小的先存到新链表中直至其中一个到尾,然后将另一个剩下的加到新链表中。

public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        //新建一个头节点,用来存合并的链表。
        ListNode head=new ListNode(-1);
        head.next=null;
        ListNode root=head;
        while(list1!=null&&list2!=null){
            if(list1.val<list2.val){
                head.next=list1;
                head=list1;
                list1=list1.next;
            }else{
                head.next=list2;
                head=list2;
                list2=list2.next;
            }
        }
        //把未结束的链表连接到合并后的链表尾部
        if(list1!=null){
            head.next=list1;
        }
        if(list2!=null){
            head.next=list2;
        }
        return root.next;
    }
}
Q17: 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

思路1:将二叉树A,B按排列依次转化为字符串A,B,查找字符串B是否在字符串A中即可

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def HasSubtree(self, pRoot1, pRoot2):
         
        def convert(p):
            if p:
                return str(p.val) +  convert(p.left) + convert(p.right)
            else:
                return ""
        return convert(pRoot2) in convert(pRoot1) if pRoot2 else False

思路2:利用依次比较两个树的结点,如果结点全部相同,则返回true,否则返回false

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/

class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot2 == NULL || pRoot1 == NULL )
            return false;
        // 比较根节点和左右结点是否相同
        return isSubtree(pRoot1, pRoot2)|| HasSubtree(pRoot1->left,pRoot2) || HasSubtree(pRoot1->right,pRoot2);
    }
      
    bool isSubtree(TreeNode* pRoot1 , TreeNode* pRoot2){
        if(pRoot2 == NULL)
            return true;
        if(pRoot1 == NULL)
            return false;
        // 移动左右结点
        return pRoot1->val == pRoot2->val && isSubtree(pRoot1->left,pRoot2->left) && isSubtree(pRoot1->right,pRoot2->right);
    }
};
Q18:操作给定的二叉树,将其变换为源二叉树的镜像。

思路:利用递归依次交换左右节点

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    void Mirror(TreeNode *pRoot) {
        if(pRoot == NULL) return;
        TreeNode* t;
        t = pRoot->left;
        pRoot->left = pRoot->right;
        pRoot->right = t;
        
        Mirror(pRoot->left);
        Mirror(pRoot->right);
    }
};
Q19:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

思路:对于一个m*n的矩阵依次打印的顺序为:(0, 1->n) -> (1->m, n) -> (m, (n-1)->0) -> (0, m->1) -> (1, 2->(n-1))…,对此可以写出如下程序,可能需要注意的是循环的圈数(我最开始没有考虑,直接m次)

class Solution {
public:
    vector<int> printMatrix(vector<vector<int> > matrix) {
        vector<int>res;
        res.clear();
        int row=matrix.size();//行数
        int collor=matrix[0].size();//列数
        //计算打印的圈数
        int circle=((row<collor?row:collor)-1)/2+1;//圈数
        for(int i=0;i<circle;i++){
            //从左向右打印
            for(int j=i;j<collor-i;j++)
                res.push_back(matrix[i][j]);         
            //从上往下的每一列数据
            for(int k=i+1;k<row-i;k++)
                res.push_back(matrix[k][collor-1-i]);
            //判断是否会重复打印(从右向左的每行数据)
            for(int m=collor-i-2;(m>=i)&&(row-i-1!=i);m--)
                res.push_back(matrix[row-i-1][m]);
            //判断是否会重复打印(从下往上的每一列数据)
            for(int n=row-i-2;(n>i)&&(collor-i-1!=i);n--)
                res.push_back(matrix[n][i]);}
        return res;
    }
};
Q20: 定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。

思路:构造两个堆栈,一个存储原数据,一个存储最小元素,即碰到比其栈顶小的元素就压栈。

class Solution {
public:
   void push(int value) {
        st.push(value);
        if(smin.empty())
            smin.push(value);
        if(smin.top()>value)
            smin.push(value);
    }
    void pop() {
        if(smin.top()==st.top())
            smin.pop();
        st.pop();
    }
    int top() {
        return st.top();
    }
    int min() {
        return smin.top();
    }
    private:
    stack<int> st;
    stack<int> smin;
};
Q21:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

思路:构造一个辅助栈,先将第一个序列中的第一个元素压入辅助栈中,判断辅助栈中的元素是否等于第二个序列的第一个元素,如果等于则辅助栈出栈,反之将第一个序列的第二个元素压入辅助栈中,再判断辅助栈中的元素是否等于第二个序列的第二个元素,依次循环判断。若最终辅助栈为空则第二个序列可能是第一个序列的弹出序列,不为空则非。

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

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

思路:构造一个队列存放结点,一个向量存放子树的值,依次将根节点左右结点存入队列中,然后遍历队列中的元素,依次将左右结点存入另一个向量中。

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        vector<int>  que;
        queue<TreeNode*>q;
        TreeNode* fr;
        if(root == NULL) return que;
        q.push(root);
        while(!q.empty())
        {
        fr=q.front();
        que.push_back(fr->val);
        //先打印左子树的左右结点,再打印右子树的左右结点
        if(fr->left != NULL)
            q.push(fr->left);
        if(fr->right != NULL)
            q.push(fr->right);
        q.pop();
        }
       return que;
    }
};
Q23:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

思路1:利用递归完成。后序遍历的左节点在右节点之前,右节点又在根节点前,而搜索二叉树的左节点值大于根节点大于右节点值,所以如果满足左子树的值都大于根节点,右子树的值都小于根节点,则是后序排列。

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        int size = sequence.size();
        if(0==size)return false;
        return isBST(sequence, 0, size-1);
    }
private:
    bool isBST(vector<int> a, int start, int end){
    if(start>=end) return true;
    int i = end;
    //找到比根节点小的结点,及左子树中最大值
    while(i>start && a[i-1]>a[end]) i--;
    for(int j = start;j<i-1;j++)
        if(a[j]>a[end])
            return false;
    //start是左子树的最小值,i-1是左子树中的最大值;i是右子树中的最小值,end-1是右子树中的最大值
    return isBST(a,start,i-1)&&isBST(a, i, end-1);
    }
};

思路2:非递归方法完成(是什么神仙代码哦),解题原理与思路1类似,关键点在于如果数组是后序排列那么只能分割为一个左子树和一个右子树,也就是说在遇到大于根节点后的结点都是大于根节点的,如果再出现小于根节点的结点那么就不说后续排列。

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        int size = sequence.size();
        if(0==size)return false;
  
        int i = 0;
        while(--size)
        {
            while(sequence[i++]<sequence[size]);
            while(sequence[i++]>sequence[size]);
  
            if(i<size)return false;
            i=0;
        }
        return true;
    }
};
Q24:输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

思路:还是递归

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
	vector<vector<int>> a;
	vector<int> c;
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
        if(root == NULL) return a;
        c.push_back(root->val);
        if((expectNumber-root->val)==0 && root->left==NULL && root->right==NULL){
        	a.push_back(c);
        }
        //二叉树中左子树值小于根小于右子树,所以在左边的值越多路径长度越长
        //这也解决了题设中要求数组长度大的数组靠前的要求
        FindPath(root->left, expectNumber-root->val);
        FindPath(root->right, expectNumber-root->val);
        //把新加入的结点去掉
        //如果c不满足条件,则去掉结点可以找正确的路径
        //如果c满足条件,去掉可以找下一条路径
        if(c.size()!=0)
        	c.pop_back();
        return a;
    }
};
Q25:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

个人感觉题目的表述并没有太明确,通过参照参考程序才知道题目到底要干嘛,相当于要得到一个复杂链表的副本,而由于这个复杂链表除了next指针外还有random指针,因此不能直接复制结点以免random指针指向出现错误。可以分为三步完成:(1)复制原链表的每一个结点的值后添加到元结点后面;(2)找到原链表中每一个结点的random指针指向,并同样设置复制后值的random指向;(3)分开原链表和复制的链表,即找到复制的链表的next指向。

/*
struct RandomListNode {
    int label;
    struct RandomListNode *next, *random;
    RandomListNode(int x) :
            label(x), next(NULL), random(NULL) {
    }
};
*/
class Solution {
public:
    RandomListNode* Clone(RandomListNode* pHead)
    {
        if (!pHead) return NULL;
    nodeClone(pHead);
    connectRandom(pHead);
    return reconnect(pHead);
    }
     
     
//[1]复制结点,插入到原结点后方
void nodeClone(RandomListNode *head)
{
    RandomListNode *pNode = head;
    while (pNode != NULL)
    {
        RandomListNode *pClone = new RandomListNode(pNode->label);
        pClone->next = pNode->next;
        
        pNode->next = pClone;
        pNode = pClone->next;
    }
}
 
//[2]还原新结点的random指针
void connectRandom(RandomListNode *head)
{
    RandomListNode *pNode = head;
     
    while (pNode != NULL)
    {
        if(pNode->random)
        pNode->next->random = pNode->random->next;
        pNode = pNode->next->next;
    }
}
 
//[3]拆分
RandomListNode *reconnect(RandomListNode *head)
{
    RandomListNode *pNode = head;
    RandomListNode *result = head->next;
    while (pNode != NULL)
    {
        RandomListNode *pClone = pNode->next;
        pNode->next = pClone->next;
        pNode = pNode->next;
        if (pNode != NULL)
            pClone->next = pNode->next;
         
    }
    return result;
}
 
};
Q25:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

个人感觉题目的表述并没有太明确,通过参照参考程序才知道题目到底要干嘛,相当于要得到一个复杂链表的副本,而由于这个复杂链表除了next指针外还有random指针,因此不能直接复制结点以免random指针指向出现错误。可以分为三步完成:(1)复制原链表的每一个结点的值后添加到元结点后面;(2)找到原链表中每一个结点的random指针指向,并同样设置复制后值的random指向;(3)分开原链表和复制的链表,即找到复制的链表的next指向。

/*
struct RandomListNode {
    int label;
    struct RandomListNode *next, *random;
    RandomListNode(int x) :
            label(x), next(NULL), random(NULL) {
    }
};
*/
class Solution {
public:
    RandomListNode* Clone(RandomListNode* pHead)
    {
        if (!pHead) return NULL;
    nodeClone(pHead);
    connectRandom(pHead);
    return reconnect(pHead);
    }
     
     
//[1]复制结点,插入到原结点后方
void nodeClone(RandomListNode *head)
{
    RandomListNode *pNode = head;
    while (pNode != NULL)
    {
        RandomListNode *pClone = new RandomListNode(pNode->label);
        pClone->next = pNode->next;
        
        pNode->next = pClone;
        pNode = pClone->next;
    }
}
 
//[2]还原新结点的random指针
void connectRandom(RandomListNode *head)
{
    RandomListNode *pNode = head;
     
    while (pNode != NULL)
    {
        if(pNode->random)
        pNode->next->random = pNode->random->next;
        pNode = pNode->next->next;
    }
}
 
//[3]拆分
RandomListNode *reconnect(RandomListNode *head)
{
    RandomListNode *pNode = head;
    RandomListNode *result = head->next;
    while (pNode != NULL)
    {
        RandomListNode *pClone = pNode->next;
        pNode->next = pClone->next;
        pNode = pNode->next;
        if (pNode != NULL)
            pClone->next = pNode->next;
         
    }
    return result;
}
 
};
Q26:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向

思路:看了一下午的题目才勉强搞懂了解法的大概意思(题目是完全没问题的,就是看不懂囧rz),首先先审题,该题是要将一个二叉搜索树转换成双向链表,且不能增加新的结点,所以我们需要在原二叉树节点数量不变的情况下确定其结点的指针指向。

二叉树的一个结点也存在两个指针:left和right,这个可以对应到双向链表的pre和next,所以需要考虑的是哪些指针需要断开,需要重新设置哪些指针,为了得到规律性的解答可以考虑树的三种遍历方式,如下图所示

[外链图片转存失败(img-hzFf0Upq-1563713460114)(C:\Users\acer\AppData\Local\Temp\1563540142751.png)]

可以发现中序遍历需要断开和增加的指针最少,同时比较有规律,因此选择中序遍历的顺序来构造双向链表较好。

// 递归版本
/*
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;
        TreeNode *pointer = NULL;
        convert2List(pRootOfTree, pointer);
        while (pointer->left!=NULL)
            pointer = pointer->left;
        return pointer;
    }
    void convert2List(TreeNode* pRoot,TreeNode *&pointer)
    {
            if (pRoot->left != NULL)
                convert2List(pRoot->left,pointer);
 
            pRoot->left = pointer;
            if (pointer != NULL)
            {
                pointer->right = pRoot;
            }
             
            pointer = pRoot;
            if (pRoot->right!=NULL)
            {
                convert2List(pRoot->right, pointer);
            }
        }
};
//非递归版本
import java.util.Stack;
    public TreeNode ConvertBSTToBiList(TreeNode root) {
        if(root==null)
            return null;
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode p = root;
        TreeNode pre = null;// 用于保存中序遍历序列的上一节点
        boolean isFirst = true;
        while(p!=null||!stack.isEmpty()){
            while(p!=null){
                stack.push(p);
                p = p.left;
            }
            p = stack.pop();
            if(isFirst){
                root = p;// 将中序遍历序列中的第一个节点记为root
                pre = root;
                isFirst = false;
            }else{
                pre.right = p;
                p.left = pre;
                pre = p;
            }      
            p = p.right;
        }
        return root;
    }
Q27:输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba

思路:递归实现,不断交换首位和后面的值,同时首位往后挪,如果字符串中存在重复的字符则交换过程中可能出现与之前重复,因此还需要判断是否有重复。

class Solution {
public:
    vector<string> Permutation(string str)
    {
        vector<string> result;
        Permutation(str,result,0);
         
        // 此时得到的result中排列并不是字典顺序,可以单独再排下序
        sort(result.begin(),result.end());
         
        return result;
    }
     
    void Permutation(string str,vector<string> &result,int begin)
    {
        if(begin == str.size()-1) // 递归结束条件:索引已经指向str最后一个元素时
        {
            // find函数找到str返回str位置,不然返回最后一个值
            if(find(result.begin(),result.end(),str) == result.end())
            {
                // 如果result中不存在str,才添加;避免aa和aa重复添加的情况
                result.push_back(str);
            }
        }
        else
        {
            // 第一次循环i与begin相等,相当于第一个位置自身交换,关键在于之后的循环,
            // 之后i != begin,则会交换两个不同位置上的字符,直到begin==str.size()-1,进行输出;
            for(int i=begin;i<str.size();++i)
            {
                swap(str[i],str[begin]);
                Permutation(str,result,begin+1);
                swap(str[i],str[begin]); // 复位,用以恢复之前字符串顺序,达到第一位依次跟其他位交换的目的
            }
        }
    }
     
    void swap(char &fir,char &sec)
    {
        char temp = fir;
        fir = sec;
        sec = temp;
    }
};
Q28:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

思路1:先将数组按大小排序,如果存在出现次数超过数组长度一半的数字,那么数组1/2位置的数一定是该数,因此只需要统计数组1/2位置出数字出现的次数即可

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers)
    {
        // 因为用到了sort,时间复杂度O(NlogN),并非最优
        if(numbers.empty()) return 0;
         
        sort(numbers.begin(),numbers.end()); // 排序,取数组中间那个数
        int middle = numbers[numbers.size()/2];
         
        int count=0; // 出现次数
        for(int i=0;i<numbers.size();++i)
        {
            if(numbers[i]==middle) ++count;
        }
         
        return (count>numbers.size()/2) ? middle :  0;
    }
};

思路2:如果有符合条件的数字,则它出现的次数比其他所有数字出现的次数和还要多。在遍历数组时保存两个值:一是数组中一个数字,一是次数。遍历下一个数字时,若它与之前保存的数字相同,则次数加1,否则次数减1;若次数为0,则保存下一个数字,并将次数置为1。遍历结束后,所保存的数字即为所求。然后再判断它是否符合条件即可。

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
    	int count = 1;
        int target = numbers[0];
        for(int i=1; i<numbers.size(); i++){
            if(numbers[i] == target)
                ++count;
            else --count;
            if(count<0)
                target = numbers[i];
        }
        count = 0;
        for(int i=0; i<numbers.size(); i++)
            if(numbers[i] == target)
                ++count;
        return (count > numbers.size()/2) ? target:0;
    }
};
Q29:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

思路1:取巧的方法,先按大小排序,然后得到最小四个

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

思路2:用最大堆保存这k个数,每次只和堆顶比,如果比堆顶小,删除堆顶,新数入堆。

import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
   public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
       ArrayList<Integer> result = new ArrayList<Integer>();
       int length = input.length;
       if(k > length || k == 0){
           return result;
       }
        PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k, new Comparator<Integer>() {
 
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2.compareTo(o1);
            }
        });
        for (int i = 0; i < length; i++) {
            if (maxHeap.size() != k) {
                maxHeap.offer(input[i]);
            } else if (maxHeap.peek() > input[i]) {
                Integer temp = maxHeap.poll();
                temp = null;
                maxHeap.offer(input[i]);
            }
        }
        for (Integer integer : maxHeap) {
            result.add(integer);
        }
        return result;
    }
}
Q30:求连续子数组的最大和

思路:使用动态规划的思想,假设当前最大子数列和为F,当前数位置为n,则比较F+n和n的大小关系,如果前者大于后者,则最大子数列可以往后挪1,反之重新开始计算,从第n+1个数开始

class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
         
        int cursum=array[0];
        int maxsum=array[0];
        for(int i=1;i<array.size();i++){
            cursum+=array[i];
            if(cursum<array[i])
                cursum=array[i];
            if(cursum>maxsum)
                maxsum=cursum;           
        }
    return maxsum;
    }
};
Q31:求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)

思路1:依次求每个数中1的个数

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        int cout = 0;
        for(int i=1; i<=n; i++){
            int temp=i;
            while(temp){
                if(temp%10 == 1)
                    cout++;
                temp /= 10;
            }
        }
        return cout;
    }
};

思路2:规则求解。对输入数字按位进行划分,求出每一位出现1(其他位不为1)的总次数。依次取位,每次相当于将数划分成两部分,以一个四位数abcd为例,十位数c求1出现的个数c相当于将c固定为1,可以分割为[ab,c, d],如果c为小于1,则十位数出现1的次数为ab*10,如果个位数大于等于1,又可以分割为等于1和大于1两种情况,等于1的话,需要考虑个位数情况,出现次数为ab*10+d+1,如果大于1的话,不需要考虑个位数情况,出现次数为(ab+1)*10.(有点解释无能,大概还是没有理太清晰)

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
    int count=0;
    long long i=1;
    for(i=1;i<=n;i*=10)
    {
        //i表示当前分析的是哪一个数位
        int a = n/i,b = n%i;
        //a+8是为了区分大于1还是等于1,如果等于1,则加上(a%10==1)*(b+1)
        count=count+(a+8)/10*i+(a%10==1)*(b+1);
    }
    return count;
    }
};
Q32:输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

关于拼接问题一般都会转化为字符串,方便操作。对数组数据进行排列,对于所有的A,B确保AB<BA(这里值得一提的是sort函数的用法,之前不知道,学习了)

class Solution {
public:
    string PrintMinNumber(vector<int> numbers) {
        int len = numbers.size();
        if(len == 0) return "";
        // 对数组按照cmp为true的方式,大胆的对sort(numbers.begin(), numbers.end())函		// 数进行猜测,可能默认函数是(A<B)默认值为true,大概可能就是这个意思。
        sort(numbers.begin(), numbers.end(), cmp);
        string res;
        for(int i = 0; i < len; i++){
            res += to_string(numbers[i]);
        }
        return res;
    }
    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;
    }
};
Q33:把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

思路:质因数只包含2,3,5,同时已知第一个数是1,所以可以将得到的数存放在一个数列中,得到所有由因子是2,3,5的数然后排序。

class Solution {
public:
   int GetUglyNumber_Solution(int index) {
        if (index < 7)return index;
        vector<int> res(index);
        res[0] = 1;
        int t2 = 0, t3 = 0, t5 = 0, i;
        for (i = 1; i < index; ++i)
        {
        	// 按从小到大排列
            res[i] = min(res[t2] * 2, min(res[t3] * 3, res[t5] * 5));
            // 数组中的数一定是2,3,5其中一个或多个数的倍数,因此在每轮循环中t1,t2,t3其			// 中必然有一个或个数会加1,整个相当于2,3,5依次与数组中之前的数相乘
            if (res[i] == res[t2] * 2)t2++;
            if (res[i] == res[t3] * 3)t3++;
            if (res[i] == res[t5] * 5)t5++;
        }
        return res[index - 1];
    }
};
Q34:在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写)

思路:构造一个映射,存放字符和对应的出现次数,可以用map或者数组完成

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        if(str.size()==0)
            return -1;
        // 初始化,所有元素都赋值0
        char ch[256]={0};
        
        for(int i=0;i<str.size();i++)
            ch[str[i]]++;
        for(int i=0;i<str.size();i++)
            if(ch[str[i]]==1)
                return i;
        return 0;     
    }
};


class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        // 使用映射
        map<char, int> mp;
        for(int i = 0; i < str.size(); ++i)
            mp[str[i]]++;
        for(int i = 0; i < str.size(); ++i){
            if(mp[str[i]]==1)
                return i;
        }
        return -1;
    }
};

思路二:可以用异或解决,用输入数与

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

思路1:看到该题第一反应是利用插入排序的思路,依次遍历数组,将第i个数与前i-1个数依次比较,累加得到总的逆序数,该方法时间复杂度较大为O(n^2)

public class Solution {
    public int InversePairs(int [] array) {
        if(array.length == 0)
            return 0;
        int num = 0;
        for(int i=1; i<array.length; i++)
            for(int j=0; j<i; j++){
                if(array[i]<array[j])
                    num += 1;
        }
        return num%1000000007;
    }
}

思路2:为了减少时间复杂度,可以考虑比较相邻的数字,参考归并排序的思想,将数组拆分为两个子数组,并进一步拆分,最终进行比较,这时时间复杂度降为O(nlogn)

#include <cstdio>

int InversePairsCore(int* data, int* copy, int start, int end);

int InversePairs(int* data, int length)
{
    if(data == nullptr || length < 0)
        return 0;

    int* copy = new int[length];
    for(int i = 0; i < length; ++i)
        copy[i] = data[i];

    int count = InversePairsCore(data, copy, 0, length - 1);
    delete[] copy;

    return count;
}

int InversePairsCore(int* data, int* copy, int start, int end)
{
    if(start == end)
    {
        copy[start] = data[start];
        return 0;
    }

    int length = (end - start) / 2;

    int left = InversePairsCore(copy, data, start, start + length);
    int right = InversePairsCore(copy, data, start + length + 1, end);

    // i初始化为前半段最后一个数字的下标
    int i = start + length;
    // j初始化为后半段最后一个数字的下标
    int j = end;
    int indexCopy = end;
    int count = 0;
    while(i >= start && j >= start + length + 1)
    {
        if(data[i] > data[j])
        {
            copy[indexCopy--] = data[i--];
            count += j - start - length;
        }
        else
        {
            copy[indexCopy--] = data[j--];
        }
    }

    for(; i >= start; --i)
        copy[indexCopy--] = data[i];

    for(; j >= start + length + 1; --j)
        copy[indexCopy--] = data[j];

    return left + right + count;
}

// ====================测试代码====================
void Test(char* testName, int* data, int length, int expected)
{
    if(testName != nullptr)
        printf("%s begins: ", testName);

    if(InversePairs(data, length) == expected)
        printf("Passed.\n");
    else
        printf("Failed.\n");
}

void Test1()
{
    int data[] = { 1, 2, 3, 4, 7, 6, 5 };
    int expected = 3;

    Test("Test1", data, sizeof(data) / sizeof(int), expected);
}

// 递减排序数组
void Test2()
{
    int data[] = { 6, 5, 4, 3, 2, 1 };
    int expected = 15;

    Test("Test2", data, sizeof(data) / sizeof(int), expected);
}

// 递增排序数组
void Test3()
{
    int data[] = { 1, 2, 3, 4, 5, 6 };
    int expected = 0;

    Test("Test3", data, sizeof(data) / sizeof(int), expected);
}

// 数组中只有一个数字
void Test4()
{
    int data[] = { 1 };
    int expected = 0;

    Test("Test4", data, sizeof(data) / sizeof(int), expected);
}


// 数组中只有两个数字,递增排序
void Test5()
{
    int data[] = { 1, 2 };
    int expected = 0;

    Test("Test5", data, sizeof(data) / sizeof(int), expected);
}

// 数组中只有两个数字,递减排序
void Test6()
{
    int data[] = { 2, 1 };
    int expected = 1;

    Test("Test6", data, sizeof(data) / sizeof(int), expected);
}

// 数组中有相等的数字
void Test7()
{
    int data[] = { 1, 2, 1, 2, 1 };
    int expected = 3;

    Test("Test7", data, sizeof(data) / sizeof(int), expected);
}

void Test8()
{
    int expected = 0;

    Test("Test8", nullptr, 0, expected);
}

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

    return 0;
}
Q36: 输入两个链表,找出它们的第一个公共结点。

看到一个大佬的代码,十分简洁,假设两个链表的长度分别为m和n,如果两个链表存在公共结点,那么在循环(m,n)的最小公倍数次之前会跳出循环;如果两个链表不存在公共结点,那么在循环(m,n)的最小公倍数次时,两个链表均指向null会跳出循环

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) {
        ListNode *p1 = pHead1;
        ListNode *p2 = pHead2;
        while(p1!=p2){
            p1 = (p1==NULL ? pHead2 : p1->next);
            p2 = (p2==NULL ? pHead1 : p2->next);
        }
        return p1;
    }
};
Q37:统计一个数字在排序数组中出现的次数。

遍历

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

二分

class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        int start,end,mid,count=0,i;
        int len = data.size();
        if(!len)
            return 0;
        start =0;
        end = len-1;
        mid = start;
        while(start<end)
        {
            mid = (start+end)>>1;
            if(k >data[mid])
                start = mid+1;
            if(k<data[mid])
                end = mid-1;
            if(k==data[mid])
                break;
        }
        i=mid;
        while( (i>=0) && (k==data[i]))
        {
            i--;
            count++;
        }
        i=mid+1;
        while((i<len)&& (k==data[i]) )
        {
            i++;
            count++;
        }
        return count;
    }
};
Q38: 输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

迭代

struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    int TreeDepth(TreeNode* pRoot)
    {
        if(pRoot==NULL) return 0;
        // 比较左右子树哪边深度大
        return max(TreeDepth(pRoot->left)+1, TreeDepth(pRoot->right)+1);
    }
};

层序遍历

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    int TreeDepth(TreeNode* pRoot)
    {
        if(pRoot==NULL) return 0;
        queue<TreeNode*> que;
        que.push(pRoot);
        int depth=0;
        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;
    }
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值