每日做题之剑指offer(五)

1.二叉搜索树与双向链表

时间限制:1秒 空间限制:32768K 热度指数:156582

题目描述

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

解题思路:

  • 在二叉搜索树中,每个节点都有两个指向子结点的指针。在双向链表中,每个节点也有两个指针,分别指向前一个结点和后一个节点。
  • 将二叉搜索树转换成排序双向链表时,原先指向左子结点的指针调整为链表中指向前一个结点的指针,原先指向右子节点的指针调整为链表中指向后一个节点的指针
  • 可以将二叉搜索树看成三部分,前面部分(左子树)的值均小于根节点,后面的部分(右子树)均大于根节点,根据这种特性,可以采取中序遍历
  • 根据中序遍历,我们可以将其按照从小到大的顺序排列好
  • 将左子树按照中序遍历转换成排序双向链表,右子树也是同理,然后根节点10将值为8的结点与自己链接起来,接着将右子树最小的节点12和根节点链接起来
  • 如何转换它的左右子树呢?可以采取递归的方式
/*
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) return NULL;
        TreeNode* p = NULL;
        help(pRootOfTree,p);
        while(pRootOfTree->left){
            pRootOfTree = pRootOfTree->left;
        }
        return pRootOfTree;
    }
    void help(TreeNode* cur,TreeNode*& pre){
        if(!cur) return;
        help(cur->left,pre);
        if(pre) pre->right = cur;
        cur->left = pre;
        pre = cur;
        help(cur->right,pre);
    }
};

2.字符串的排列

时间限制:1秒 空间限制:32768K 热度指数:241299

本题知识点: 字符串

题目描述

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

输入描述:

输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

解题思路:

  • 把字符串分成两部分:一部分是字符串的第一个字符
  • 另一部分是第一个字符以后的所有的字符,然后求该部分的字符排列
  • 然后拿第一个字符和它后面的字符逐个交换
# -*- coding:utf-8 -*-
import itertools
class Solution:
    def Permutation(self, ss):
        # write code here
        if not ss:
            return []
        return sorted(list(set(map(''.join, itertools.permutations(ss)))))
        

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

时间限制:1秒 空间限制:32768K 热度指数:210161

本题知识点: 数组

题目描述

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

解题思路:

  • 我们可以在遍历数组的时候保存两个值:一个是数组中的一个数字,另一个是次数
  • 当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加一;否则次数减1
  • 如果次数减为0,说明不是我们要找的数字,这时就需要保存下一个数字,并将次数重新设置为1
  • 因为我们要找的数字超过数组长度的一半,那么如果最后哪个数字的次数减为1那么该数字就是我们要找的数字
# -*- coding:utf-8 -*-
class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        if not numbers:
            return 0
        result = numbers[0]
        times = 1
        length = len(numbers)
        for i in range(1, length-1):
            print times
            if times == 0:
                result = numbers[i+1]
            elif result == numbers[i+1]:
                times += 1
            else:
                times -= 1
        if numbers.count(result)*2 > length:
            return result
        return 0

4.最小的K个数

时间限制:1秒 空间限制:32768K 热度指数:268814

本题知识点: 数组

题目描述

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

解题思路:

解法一:(时间复杂度为O(n), 只有当我们可以修改输入的数组时可用)

  • 基于数组的第K个数字来调整,使得比第K个数字小的所有数字都位于数组的左边
  • 使得比第K个数字大的所有数字都位于数组的右边
  • 这样调整后,位于数组中左边的K个数字就是最小的K个数字,但是序列不一定是排序的
class Solution {
public:
     int partion(vector<int>& input, int beg, int end)
    {
        int key = input[beg];
        while (beg < end)
        {
            while (beg < end && input[end] >key)
                end--;
            input[beg] = input[end];
           
            while (beg < end && input[beg] <= key)
                beg++;
            input[end] = input[beg];
             
        }
        input[beg] = key;
        return beg;
    }
    
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        if (input.size() == 0 || input.size() < k || k <= 0)
            return {};
        
        int low = 0;
        int high = input.size()-1;
        int pos = partion(input, low, high);
        while (pos != k - 1)
        {
            if (pos > k - 1)
            {
                high=pos-1;
                pos = partion(input, low, high);
            }  
            else
            {
                low=pos+1;
                pos = partion(input, low, high);
            }
                 
        }
        vector<int> res(input.begin(), input.begin() + k);
        return res;
    }
};

解法二;(时间复杂度为O(nlogk)的算法,特别适合处理海量数据)

  • 我们可以创建一个大小为K的数据容器来存储最小的K个数字,接下来每次从输入的n个整数中读入一个数
  • 如果容器中已有的数字小于K个,则直接放入容器;如果容器中已有K个数字,此时我们已经不能插入而是应该替换了
  • 这时就要找出容器中的最大值,然后将最大值与待插入的数字进行比较
  • 如果小,就替换当前的最大值;如果大,就不替换,继续和下一个即将插入的数字进行比较
  • 这个容器我们可以使用堆·
class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        int len = input.size();
        vector<int> result;
        
        if(len <= 0 || k <= 0 || len < k)
            return result;
        
        for(int i = 0;i < k; ++i)
            result.push_back(input[i]);
        
        make_heap(result.begin(),result.end());
        
        for(int i= k;i < len; ++i)
            if(result[0] > input[i]){
                pop_heap(result.begin(),result.end());
                result.pop_back();
                result.push_back(input[i]);
                push_heap(result.begin(),result.end());
            }
        sort_heap(result.begin(),result.end());
        return result;
    }
};

5.连续子数组的最大和

时间限制:1秒 空间限制:32768K 热度指数:148691

本题知识点: 数组

题目描述

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

解题思路:

  • 此处以数组{1,-2,3,10,-4,7,2,-5}为例
  • 前面的就不赘述了,表格里面有,我们从3开始分析,此时得到的和为3,(表格里面)第四步加10,和为13
  • 第五步加-4,和为9,小于前面的13,将其保存下来,有可能就是最大的
  • 第六步加7,和为16,比13大
  • 第七步加2,和为18,比16大
  • 第八步加-5,和为13,小于18,所以18就是最大值,对应的连续子数组为{3,10,-4,7,2}
class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
        if(array.size() <= 0)
            return 0;
        
        int currentSum = arAray[0];
        int maxSum = array[0];
        int len = array.size();
        for(int i = 1; i < len; ++i)
        {
            currentSum += array[i];
            if(currentSum < array[i])
                currentSum = array[i];
            if(currentSum > maxSum)
                maxSum = currentSum;
        }
        return maxSum;
    }
};

6.整数中1出现的次数(从1到n整数中1出现的次数)

时间限制:1秒 空间限制:32768K 热度指数:117393

题目描述

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

解题思路:

(一)采取暴力解决,效率比较低下,时间复杂度为O(nlogn)

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

(二)

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        '''
        思路:X∈[1,9],0不适用
        从 1 至 10,在它们的个位数中,任意的 X 都出现了 1 次。
        从 1 至 100,在它们的十位数中,任意的 X 都出现了 10 次。
        从 1 至 1000,在它们的百位数中,任意的 X 都出现了 100 次。
        依此类推,从 1 至  10^i ,在它们的左数第二位(右数第  i  位)中,任意的 X 都出现了  10^(i−1)  次。
        '''
        x=1
        if n<0 or x<=0 or x>9:
            return 0
        high=-1;low=-1;time=0
        i=1#i为数字n从右往左的第i位
        while high!=0:
            high = n / (10 ** i)  # 拿到高位
             
            temp = n % (10 ** i)
            occur = temp / (10 ** (i - 1))  # 拿到第i位上的数字
             
            low = n % (10 ** (i - 1))#拿到i的低位
             
            time+=high*(10**(i-1))#先算基础值,再加上低位的进行补充
            if occur>x:#比x大,把第i位的次数加上
                time=time+10**(i-1)
            elif occur==x:
                time=time+low+1
            i+=1
        return time

猜你喜欢

转载自blog.csdn.net/ijn842/article/details/81611313