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

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

class Solution:
    def FindGreatestSumOfSubArray(self, array):
        res =len(array) and max(array)       #长度不为零,则reszan定位array的最大值
        temp = 0 
        for i in array:
            temp = max(i,temp+i)             #如果之前的值加上现在比现在还小,则舍去之前的
            res = max(res,temp)              #比较最大的res与最新的temp,留存最大值
        return res 

动态规划https://blog.csdn.net/baidu_28312631/article/details/47418773 

https://www.zhihu.com/question/23995189 (推荐)

https://blog.csdn.net/u013309870/article/details/75193592

核心思想:尽量缩小可能解空间。

操作过程 ;一言以蔽之:大事化小,小事化了。

  将一个大问题转化成几个小问题;
  求解小问题;
  推出大问题的解。

重要概念:无后效性(让我想起来控制学过的的马尔科夫过程...),最优子结构

因此判断能否用动态规划:能将大问题拆成几个小问题,且满足无后效性、最优子结构性质

拓扑排序入门:https://blog.csdn.net/qq_41713256/article/details/80805338

动规的优点:暴力做法是枚举所有的可能解,这是最大的可能解空间。DP是枚举有希望成为答案的解。这个空间比暴力的小得多。

也就是说:DP自带剪枝。

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

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        if n ==0:
            return 0
        res = []
        num = 0
        for i in range(n+1):
            res.append(str(i))
        for j in res:
            num = num + j.count('1')
        return num
                

暴力法:转换成str类型,然后分别计算‘1’的个数。注意 str ,count的用法

一定要注意range(len(s))与range(s)的区别,以及对应下 i 的不同含义

也可以写成

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

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        return ''.join(map(str, range(n+1))).count('1')

除开暴力法,要用归纳找规律的方法来解决

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

/*
设N = abcde ,其中abcde分别为十进制中各位上的数字。
如果要计算百位上1出现的次数,它要受到3方面的影响:百位上的数字,百位以下(低位)的数字,百位以上(高位)的数字。
① 如果百位上数字为0,百位上可能出现1的次数由更高位决定。比如:12013,则可以知道百位出现1的情况可能是:100~199,1100~1199,2100~2199,,...,11100~11199,一共1200个。可以看出是由更高位数字(12)决定,并且等于更高位数字(12)乘以 当前位数(100)。
② 如果百位上数字为1,百位上可能出现1的次数不仅受更高位影响还受低位影响。比如:12113,则可以知道百位受高位影响出现的情况是:100~199,1100~1199,2100~2199,,....,11100~11199,一共1200个。和上面情况一样,并且等于更高位数字(12)乘以 当前位数(100)。但同时它还受低位影响,百位出现1的情况是:12100~12113,一共114个,等于低位数字(113)+1。
③ 如果百位上数字大于1(2~9),则百位上出现1的情况仅由更高位决定,比如12213,则百位出现1的情况是:100~199,1100~1199,2100~2199,...,11100~11199,12100~12199,一共有1300个,并且等于更高位数字+1(12+1)乘以当前位数(100)。
*/ 
public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        int count = 0;//1的个数
        int i = 1;//当前位
        int current = 0,after = 0,before = 0;
        while((n/i)!= 0){           
            current = (n/i)%10; //高位数字
            before = n/(i*10); //当前位数字
            after = n-(n/i)*i; //低位数字
            //如果为0,出现1的次数由高位决定,等于高位数字 * 当前位数
            if (current == 0)
                count += before*i;
            //如果为1,出现1的次数由高位和低位决定,高位*当前位+低位+1
            else if(current == 1)
                count += before * i + after + 1;
            //如果大于1,出现1的次数由高位决定,//(高位数字+1)* 当前位数
            else{
                count += (before + 1) * i;
            }    
            //前移一位
            i = i*10;
        }
        return count;
    }
}

2 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

# -*- coding:utf-8 -*-
class Solution:
    def PrintMinNumber(self, numbers): #类似冒泡排序
        # write code here
        if not numbers:
            return ''
        for j in range(len(numbers)):
            for i in range(len(numbers)-1):
                a = str(numbers[i+1]) + str(numbers[i])
                b = str(numbers[i]) + str(numbers[i+1])
                if a > b:
                    tmp = numbers[i]
                    numbers[i] = numbers[i+1]
                    numbers[i+1] = tmp
                else:
                    pass
        res = ''
        for i in range(len(numbers)):
            res = str(numbers[i]) + res
        res1 = int(res)
        return res1
         

排序算法的升级版,比较的不是两个数的大小,而是组成字符串(数字)的大小,上面使用了冒牌排序来解决。摘录于评论区一段话“所以在这里自定义一个比较大小的函数,比较两个字符串s1, s2大小的时候,先将它们拼接起来,比较s1+s2,和s2+s1那个大,如果s1+s2大,那说明s2应该放前面,所以按这个规则,s2就应该排在s1前面。突然大悟。”

4 把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

一开始使用暴力法,即判断每一个数是不是丑数来解决,但是时间超时了(肯定的..)

 '''res = []
        i = 0
        while(len(res) < index):
            i = i+1
            if i %2 ==0 or i%3 ==0 or i%5 ==0 or i ==1 :
                r = self.ugly(i)
                if r > 0:
                    res.append(self.ugly(i))
                else:
                    pass
            else:
                pass
        return res[-1]
    def ugly(self,i):
        flag = 0
        i_old = i
        while(flag == 0):
            if i%2  == 0:
                i = int(i/2)
            elif i%3 ==0:
                i = int(i/3)
            elif i % 5 ==0:
                i = int(i/5)
            elif i == 1:
                flag = 1
            else:
                flag = 1
        if i == 1:
            return i_old
        else:
            return -1''' 

应该改用生成法:即通过乘法运算算出丑数序列。这里涉及到一个排序的问题,用一个min函数圆满解决。

if index ==0:
            return 0
        res = [1]
        t2 = t3 = t5 = 0
        while (len(res) < index):
            r = min(2*res[t2],3*res[t3],5*res[t5])
            if r not in res:
                res.append(r)
            if min(2*res[t2],3*res[t3],5*res[t5]) == 2*res[t2]:
                t2 = t2+1
            elif min(2*res[t2],3*res[t3],5*res[t5]) == 3*res[t3]:
                t3 = t3+1
            else:
                t5 = t5+1
        return res[-1] 
                

t起到类似于指针的作用,用于指示进行运算的res值,每个res只能进行一次2,3,5运算,所以运算完成将t后移。

5 在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).

一开始的解法:

if not s:
            return -1
        for i in range(len(s)):
            flag = self.findone(s[i],s)
            if flag:
                return i
        return -1
    def findone(self,num,s):
        fre = 0
        for j in range(len(s)):
            if s[j] == num:
                fre = fre +1
            else :
                pass
        if fre == 1:
            return True
        else:
            return False

但是显然本地可以利用dict来解决啊(正好重温dict用法),即遍历一遍之后把各个数字在数组中的出现次数存储起来

res =  dict.fromkeys(s,0)
        for i in s:
            res[i] = 1 + res[i]
            #print(res)
        for j in s:
            if res[j] == 1:
                return s.index(j)

注意dict的fromkeys生成方法,注意初始化值

注意index的用法

6 输入两个链表,找出它们的第一个公共结点。

思路是。利用两个链表之差,来解决,一个很妙的方法是两指针同时遍历,短的肯定先循环完,之后调换指针,显然指针之差正好是长度差,因此减少了计算长度差这一步。当然也可以用栈倒序做,或者先求长度差,然后长链表先跑len的差后再进行比较。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def FindFirstCommonNode(self, pHead1, pHead2):
        # write code here
        p1,p2 = pHead1,pHead2
        while(p1 != p2 ):
            p1 = p1.next if p1 else pHead2
            p2 = p2.next if p2 else pHead1
        return p1
            
'''     len1 = len2 = 0
        p1 = pHead1
        p2 = pHead2
        if not pHead1 or not pHead2:
            return None
        while(p1.next):
            len1 = len1 + 1
            p1 = p1.next
        while(p1.next):
            len2 = len2 + 1
            p1 = p1.next
        dis  = abs(len1 - len2)
        if len1 > len2:
            for i in range(dis):
                pHead1 = pHead1.next
            for i in range(len1 - dis):
                if pHead1 = pHead2:
                    return pHead1
            return None
        elif dis == 0:
            for i in range(len1 - dis):
                if pHead1 = pHead2:
                    return pHead1
            return None
        else:
            for i in range(len1):
                if pHead1 = pHead2:
                    return pHead1
            return None
            '''

链表题:注意链表的定义 注意头指针的copy 注意比较跟返回的是val还是结点本身

7 统计一个数字在排序数组中出现的次数。

# -*- coding:utf-8 -*-
class Solution:
    def GetNumberOfK(self, data, k):
        # write code here
        count = 0
        Flag = False
        if not data:
            return 0
        for i in range(len(data)):
            if data[i] == k:
                Flag =True
                count = 1
                j = 1
                while(j+i < len(data)):  #不能是小于等于,因为最后一位是 data[n-1],n是长度
                    print(i+j)
                    if data[j+i] == data[i]:
                        count = count + 1
                    j = j + 1
                return count
        return count

 暴力稍微简化法如上。注意i,j以及data[i],data[j]的区别!

评论区大佬:看见有序,肯定就是二分查找了,算法比较简单,不多说,值得一提的是,不要拘泥于递归,要会循环写法。——这个意思要有,划重点要考的...

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

def GetFirstK(self, data, k):
    low = 0
    high = len(data) - 1
    while low <= high:
        mid = (low + high) // 2
        if data[mid] < k:
            low = mid + 1
        elif data[mid] > k:
            high = mid - 1
        else:
            if mid == low or data[mid - 1] != k: #当到list[0]或不为k的时候跳出函数
                return mid
            else:
                high = mid - 1
    return -1
 
def GetLastK(self, data, k):
    low = 0
    high = len(data) - 1
    while low <= high:
        mid = (low + high) // 2
        if data[mid] > k:
            high = mid - 1
        elif data[mid] < k:
            low = mid + 1
        else:
            if mid == high or data[mid + 1] != k:
                return mid
            else:
                low = mid + 1
    return -1
 
def GetNumberOfK(self, data, k):
    if not data:
        return 0
    if self.GetLastK(data, k) == -1 and self.GetFirstK(data, k) == -1:
        return 0
    return self.GetLastK(data, k) - self.GetFirstK(data, k) + 1
 

基础知识补充:

1 Python ord() 函数 ord() 函数是 chr() 函数(对于8位的ASCII字符串)或 unichr() 函数(对于Unicode对象)的配对函数,它以一个字符(长度为1的字符串)作为参数,返回对应的 ASCII 数值,或者 Unicode 数值,如果所给的 Unicode 字符超出了你的 Python 定义范围,则会引发一个 TypeError 的异常。>>>ord('a') 97 >>> ord('b') 98 >>> ord('c') 99

 

 

2 Python 字典(Dictionary) fromkeys()方法  Python 字典 fromkeys() 函数用于创建一个新字典,以序列 seq 中元素做字典的键,value 为字典所有键对应的初始值。

romkeys()方法语法:

dict.fromkeys(seq[, value])

3 python字典的用法 http://www.runoob.com/python/python-dictionary.html

4 and (逻辑与)的计算法则 

在程序设计中,and称为逻辑与运算,也称布尔运算;
    1.and是在布尔上下文中从左到右计算表达式的值;
    2.0、''、[]、()、{}、None、False在布尔上下文中为假;其它任何东西都为真;
    3.如果布尔上下文中的某个值为假,则返回第一个假值;
    4.所有值都为真,则返回最后一个真值。

5 通常取模运算也叫取余运算,它们返回结果都是余数 .rem 和 mod 唯一的区别在于:

当 x 和 y 的正负号一样的时候,两个函数结果是等同的;当 x 和 y 的符号不同时,rem 函数结果的符号和 x 的一样,而 mod 和 y 一样。

6 DP,DFS,贪心等等要深入研究,尤其是背包问题跟路径规划之类的题目。