目录

第 21题:

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

解题思路:

其实这就是一个二叉树层序遍历的问题,我们可以有以下两种方法来实现

  • 使用辅助队列完成对二叉树的层序遍历
  • 使用数组辅助来完成对二叉树的辅助遍历

代码实现:

c++

class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        vector<int> printArray; //用于保存输出的节点
        queue<TreeNode*>  tempQueue; //辅助队列
        if(root != NULL){
            tempQueue.push(root);
        }
        while(!tempQueue.empty()){  //只要队列不空就可以数据
            if(tempQueue.front()->left != NULL){  //遍历左子树
                tempQueue.push(tempQueue.front()->left);
            }
            if(tempQueue.front()->right != NULL){ //遍历右子树
                tempQueue.push(tempQueue.front()->right);
            }
            printArray.push_back(tempQueue.front()->val);  //将要打印的东西输出
            tempQueue.pop();
        }
        return printArray; 
    }
};

运行时间:3ms

占用内存:604k

python

# -*- coding:utf-8 -*-
class Solution:
    # 返回从上到下每个节点值列表,例:[1,2,3]
    def PrintFromTopToBottom(self, root):
        # write code here
        if not root:
            return []
        currentStack = [root]  #用于保存双亲节点的列表
        res = []
        while currentStack:
            nextStack = []  #用于记录当前双亲节点下的孩子节点
            for i in currentStack:
                if i.left: nextStack.append(i.left)  #左孩子存在,则将左孩子保存下来
                if i.right: nextStack.append(i.right) #右孩子存在,则将右孩子保存下来
                res.append(i.val)
            currentStack = nextStack  #更新双亲节点
        return res

运行时间:30ms

占用内存:5700k

第22 题:

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

解题思路:

BST的后序序列的合法序列是,对于一个序列S,最后一个元素是x (也就是根),如果去掉最后一个元素的序列为T,那么T满足:T可以分成两段,前一段(左子树)小于x,后一段(右子树)大于x,且这两段(子树)都是合法的后序序列。完美的递归定义 : ) 。

代码实现:

c++

链接:https://www.nowcoder.com/questionTerminal/a861533d45854474ac791d90e447bafd
来源:牛客网

public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence.length == 0){
            return false;
        }
        if(sequence.length == 1){
            return true;
        }
        return judge(sequence,0,sequence.length-1);
    }
     //判断是否为二叉搜索树,采用分治思想
    public boolean judge(int[] a,int start,int end){
        if(start >= end){//当二叉树的左子树为空时,肯定是一个二叉搜索树
            return true;
        }
        int i = start;  
        //找到第一个比root节点大的元素的位置,便是右子树的起始位置
        while(a[i] < a[end]){
            ++i;
        }
        //判断右子树是否都比root节点大
        for(int j=i;j<end;j++){
            if(a[j] < a[end]){
                return false;  //存在不大的情况则为false
            }
        }
        return judge(a,start,i-1) && judge(a,i,end-1); //没看懂这部分
    }
};

运行时间:4ms

占用内存:480k

python

# -*- coding:utf-8 -*-

第23 题:

输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

解题思路:

  • 这其实是一道树的深度优先遍历的算法题,我们可以通过带有记忆的深度优先算法来做。

代码实现:

c++

class Solution {
public:
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
        vector<vector<int> > res;   //用于保存最终的路径结果
        if (root == NULL)
            return res;
        stack<TreeNode *> s;  // 辅助深度优先遍历二叉树的栈
        s.push(root); 
        int sum = 0; //当前和
        vector<int> curPath; //当前路径
        TreeNode *cur = root; //当前节点
        TreeNode *last = NULL; //保存上一个节点
        while (!s.empty()){
            if (cur == NULL){
                TreeNode *temp = s.top();
                if (temp->right != NULL && temp->right != last){
                    cur = temp->right; //转向未遍历过的右子树
                }else{
                    last = temp; //保存上一个已遍历的节点
                    s.pop();
                    curPath.pop_back(); //从当前路径删除
                    sum -= temp->val;
                }  }
            else{
                s.push(cur);
                sum += cur->val;
                curPath.push_back(cur->val);
                if (cur->left == NULL && cur->right == NULL && sum == expectNumber){
                    res.push_back(curPath);  //保存正确的路径
                }
                cur = cur->left; //先序遍历,左子树先于右子树
            }
        }
        return res;
    }
};

运行时间:3ms

占用内存:376k

python

# -*- coding:utf-8 -*-

第24 题:

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

解题思路:

剑指offer(刷题21-30)--c++,Python版本

代码实现:

c++

非递归实现
class Solution {
public:
    //复制原始链表的任一节点N并创建新节点N',再把N'链接到N的后边
    void CloneNodes(RandomListNode* pHead)
    {
        RandomListNode* pNode=pHead;
        while(pNode!=NULL)
        {
            RandomListNode* pCloned=new RandomListNode(0);
            pCloned->label=pNode->label;
            pCloned->next=pNode->next;
            pCloned->random=NULL;
              
            pNode->next=pCloned;
              
            pNode=pCloned->next;
        }
    }
    //如果原始链表上的节点N的random指向S,则对应的复制节点N'的random指向S的下一个节点S'
    void ConnectRandomNodes(RandomListNode* pHead)
    {
        RandomListNode* pNode=pHead;
        while(pNode!=NULL)
        {
            RandomListNode* pCloned=pNode->next;
            if(pNode->random!=NULL)
                pCloned->random=pNode->random->next;
            pNode=pCloned->next;
        }
    }
    //把得到的链表拆成两个链表,奇数位置上的结点组成原始链表,偶数位置上的结点组成复制出来的链表
    RandomListNode* ReConnectNodes(RandomListNode* pHead)
    {
        RandomListNode* pNode=pHead;
        RandomListNode* pClonedHead=NULL;
        RandomListNode* pClonedNode=NULL;
          
        //初始化
        if(pNode!=NULL)
        {
            pClonedHead=pClonedNode=pNode->next;
            pNode->next=pClonedNode->next;
            pNode=pNode->next;
              
        }
        //循环
        while(pNode!=NULL)
        {
            pClonedNode->next=pNode->next;
            pClonedNode=pClonedNode->next;
            pNode->next=pClonedNode->next;
            pNode=pNode->next;
        }
          
        return pClonedHead;
          
    }
    //三步合一
    RandomListNode* Clone(RandomListNode* pHead)
    {
        CloneNodes(pHead);
        ConnectRandomNodes(pHead);
        return ReConnectNodes(pHead);
    }
};

运行时间:4ms

占用内存:484k

递归实现
链接:https://www.nowcoder.com/questionTerminal/f836b2c43afc4b35ad6adc41ec941dba
来源:牛客网

/*递归思想:把大问题转化若干子问题
  此题转化为一个头结点和除去头结点剩余部分,剩余部分操作和原问题一致
*/
class Solution {
public:
    RandomListNode* Clone(RandomListNode* pHead)
    {
        if(pHead==NULL)
            return NULL;
         
        //开辟一个新节点
        RandomListNode* pClonedHead=new RandomListNode(pHead->label);
        pClonedHead->next = pHead->next;
        pClonedHead->random = pHead->random;
         
        //递归其他节点
        pClonedHead->next=Clone(pHead->next);
         
        return pClonedHead;
    }
};

python

# -*- coding:utf-8 -*-

第25题:

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

解题思路:

  • 初步的想法:使用一个队列辅助进行中序遍历,然后让节点出队的时候进行节点的调整;具体的做法是:使用队列保存该二叉搜索树的中序遍历结果,然后在将队列中的节点一个一个进行出队,出队的同时将节点的指针进行调整,使得左节点指向后继,右节点指向前驱。

代码实现:

c++

class Solution {
public:
    TreeNode* Convert(TreeNode* pRootOfTree)
    {
        if(pRootOfTree == nullptr) return nullptr;
        TreeNode* pre = nullptr;
        convertHelper(pRootOfTree, pre);
        TreeNode* res = pRootOfTree;
        while(res ->left)
            res = res ->left;
        return res;
    }
     //中序遍历即可。只需要记录一个pre指针即可。
    void convertHelper(TreeNode* cur, TreeNode*& pre)
    {
        if(cur == nullptr) return;
         
        convertHelper(cur ->left, pre);
         
        cur ->left = pre;
        if(pre) pre ->right = cur;
        pre = cur;
        convertHelper(cur ->right, pre);     
    }
};

运行时间:3ms

占用内存:472k

python

# -*- coding:utf-8 -*-
class Solution:
    def Convert(self, pRootOfTree):
        # write code here
        if not pRootOfTree:return
        self.arr = []
        self.midTraversal(pRootOfTree)
        for i,v in enumerate(self.arr[:-1]):
            v.right = self.arr[i + 1]
            self.arr[i + 1].left = v
        return self.arr[0]
 
    def midTraversal(self, root):
        if not root: return
        self.midTraversal(root.left)
        self.arr.append(root)
        self.midTraversal(root.right)

第26 题:

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

解题思路:

代码实现:

c++

class Solution {
public:
    vector<string> Permutation(string str)
    {
        vector<string> result;
        if(str.empty()) return 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最后一个元素时
        {
            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;
    }
};

运行时间:5ms

占用内存:612k

python

# -*- coding:utf-8 -*-

第27 题:

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

解题思路:

  • 初步的想法:构建一个map映射表,key为数组中元素的值,value为出现的次数,运行的时间复杂度为O(N);

代码实现:

c++

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        if(numbers.size()<=0){
		    return -1;
        }
        map<int,int> tempDataDict;
        for(int i = 0 ;i < numbers.size(); i++){
            if (tempDataDict.find(numbers[i]) == tempDataDict.end()){
                tempDataDict[numbers[i]] = 1;
            }else{
                tempDataDict[numbers[i]] += 1;
            }
        }
        for (map<int, int>::iterator iter = tempDataDict.begin();  iter != tempDataDict.end();  ++iter){
            if(iter->second > numbers.size() * 0.5){
                return iter->first;
            }
        }
        return 0; 

        }
};

运行时间:4ms

占用内存:480k

python

# -*- coding:utf-8 -*-
# -*- coding:utf-8 -*-
class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        tempDict = {}
        for data in numbers:
            if data not in tempDict.keys():
                tempDict[data] = 1
            else:
                tempDict[data] += 1
        for key , value in tempDict.items():
            if value > len(numbers)/2:
                return key
        return 0

运行时间:28ms

占用内存:5852k

第28题:

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

解题思路:

  • 先对这n个整数进行从小到大排序,然后取前K个数字就行。

代码实现:

c++

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
       vector<int> resultArray;
        if(input.size() < k){
            return resultArray;
        }     
        sort(input.begin(),input.end());
        resultArray.assign(input.begin(), input.begin()+k);
        return resultArray; 
    }
};

运行时间:5ms

占用内存:460k

python

# -*- coding:utf-8 -*-
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        if len(tinput) < k:
            return []
        tinput.sort()
        return tinput[:k]

运行时间:20ms

占用内存:5856k

第29题:

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

解题思路:

使用动态规划
F(i):以array[i]为末尾元素的子数组的和的最大值,子数组的元素的相对位置不变
F(i)=max(F(i-1)+array[i] , array[i])
res:所有子数组的和的最大值
res=max(res,F(i))

如数组[6, -3, -2, 7, -15, 1, 2, 2]
初始状态:
F(0)=6
res=6
i=1:
F(1)=max(F(0)-3,-3)=max(6-3,3)=3
res=max(F(1),res)=max(3,6)=6
i=2:
F(2)=max(F(1)-2,-2)=max(3-2,-2)=1
res=max(F(2),res)=max(1,6)=6
i=3:
F(3)=max(F(2)+7,7)=max(1+7,7)=8
res=max(F(2),res)=max(8,6)=8
i=4:
F(4)=max(F(3)-15,-15)=max(8-15,-15)=-7
res=max(F(4),res)=max(-7,8)=8
以此类推
最终res的值为8

代码实现:

c++

class Solution {
public:
	//找最大值函数
    int fMax(int a , int b){
        return a>b?a:b;
    }
    //找最大子序列的和
    int FindGreatestSumOfSubArray(vector<int> array) {
        int res = array[0]; //记录当前所有子数组的和的最大值
        int max=array[0];   //包含array[i]的连续数组最大值
        for (int i = 1; i < array.size(); i++) {
            max=fMax(max+array[i], array[i]); 
            res=fMax(max, res);
        }
        return res;
    }
};

运行时间:3ms

占用内存:472k

python

# -*- coding:utf-8 -*-
class Solution:
    
    def fMax(self,a , b ):
        if a>b:
            return a
        else:
            return b
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        res = array[0]
        tempMaxValue = array[0]
        for i in range(1,len(array)):
            tempMaxValue = self.fMax(tempMaxValue + array[i] , array[i])
            res = self.fMax(tempMaxValue , res)
        return res

运行时间:29ms

占用内存:5664k

第30题:

求出113的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

解题思路:

  • 初步思想:对数字进行遍历,然后将数字转换为字符数组类型,然后统计字符数组中1的个数即可,时间复杂度0(n*m),使用Python非常容易实现。
  • 参考:

注解:参考一位牛友提到的leetcode的链接网址(包括求1~n的所有整数中2,3,4,5,6,7,8,9出现的所有次数)
通过使用一个 位置乘子m 遍历数字的位置, m 分别为1,10,100,1000…etc.(m<=n)

对于每个位置来说,把10进制数分成两个部分,比如说 当m=100的时候, 把十进制数 n=3141592 分成 a=31415 和 b=92 ,以此来分析百位数为1时所有数的个数和。m=100时,百位数的前缀为3141,当百位数大于1时,为3142100,因为当百位数大于1时,前缀可以为0,即百位数可以从100到199,共100个数;当百位数不大于1时,为3141100;如何判断百位数是否大于1?假设百位数为x,若(x+8)/10等于1,则大于1,若(x+8)/10等于0,则小于1。因此前缀可用(n/m + 8)/10 m来计算(若计算2的个数,可以改为(n/m + 7)/10m,若计算3的个数,改为(n/m + 6)/10*m,…以此类推)。

再例如m=1000时,n分为a=3141和 b=592;千位数的前缀为314,千位数不大于1,故前缀计算为3141000;因为千位数为1,再加b+1(0到592)。即千位数为1的所有书的个数和为3141000+592+1;公式(n/m + 8)/10*m + b +1。

注意:只有n的第m位为1时需要计算后缀,后缀计算为 (n/m%10==1)*(b+1),

即(n/m%101)判断第m位是否为1,若为1,则加上(b+1),若不为1,则只计算前缀。(若计算2的个数,可以改为(n/m%102)(b+1),若计算3的个数,可以改为(n/m%10==3)(b+1)…以此类推)

代码实现:

c++

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
    	int ones = 0;
    	for (long long m = 1; m <= n; m *= 10) {
        	int a = n/m, b = n%m;
        	ones += (a + 8) / 10 * m + (a % 10 == 1) * (b + 1);
    	}
        return ones;
    }
};

运行时间:3ms

占用内存:488k

python

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        if n == 0:
            return 0
        count = 0
        for i in range(n+1):
            tempList = list(str(i))
            for j in tempList:
                if j == "1":
                    count += 1
        return count

运行时间:52ms

占用内存:5856k

相关文章: