python leetcode1

13. 罗马数字转整数

我也不知道为什么这个可以通过,没细想.
我是采用两个一比对,这样的话需要看一看最后一位取的到吗?

# 首先的想法:
#     1.他已经说明输入一定为正确的罗马数字(字符串形式)
#     2.将六种特殊情况现在字符串中排查一遍,转化成数字,并在字符串中删除
#     3.将其他字符将字符串中转化为数字


def fun1(s):
    #创建一个字典
    dic_liu={
    
    'IV':4,'IX':9,'XL':40,'XC':90,'CD':400,'CM':900}
    dic_={
    
    'I':1,'V ':5,'X ':10,'L':50,'C':100,'D':500,'M':1000}

    #用来求和的变量
    sum_=0

    #用来记录当前位置的变量
    i=0
    if len(s)==1:
        return dic_[s]
    while i<len(s)-1:
        key=s[i:i+2:]
        print(key)
        if key in dic_liu.keys():
            sum_+=dic_liu[key]
            if i+2==len(s)-1:
                sum_+=dic_[s[i+2]]
            i+=2
        else:
            sum_+=dic_[key[0]]
            if i+1==len(s)-1:
                sum_+=dic_[s[i+1]]
            i+=1

    return sum_

def fun2(s):
    
    #就是什么样的情况-,什么样的情况+
    dic_={
    
    
        'I': 1,
        'V': 5,
        'X': 10,
        'L': 50,
        'C': 100,
        'D': 500,
        'M': 1000,
    }

    sum_=0
    n=len(s)
    for i,ch in enumerate(s):
        item=dic_[ch]
        #输入的一定是正确的
        if i<n-1 and item<dic_[s[i+1]]:#这样判断的前提是输入是''正确''的
            sum_-=item
        else:
            sum_+=item
    return sum_

=============================================================================================================================================================================================================================================================================================

14. 最长公共前缀

#首先的想法是每一次将数组中的遍历一遍
#将首位进行比对,如果都一样,在继续比对第二位

def fun1(strs):
    if len(strs)==1:
        return strs[0]
    first=strs[0]
    for i in range(len(first)):#总共遍历数组的次数
        for j in range(1,len(strs)):#把数组走一遍
            if i<len(strs[j]):
                if first[i]!=strs[j][i]:
                    #不相等时进来
                    #假设当前i等于2,后面的代码又是return
                    #说明前面都没有进来过,也就是说first的第0位和第1位与其他元素均一样
                    #这时候i==2在与某个元素判断时却进来了,那么就不可能继续往后面走了
                    return first[0:i:]
            else:
                #i和len(strs[j])只有两种情况小于或等于
                #进来的一定是等于情况
                #能进来说明此时i对应的数字大于等于当前元素的长度,所以
                return first[0:i:]
    return first






#又有了一种想法,先将字符串数组中的第一个和第二个拿出来比对一下
    #1.如果没有共同前缀,则直接输出没有
    #2.如果有公共前缀(假设为flx),那么最长公共前缀长度一定小于等于flx
    #3.通过上述可以减少比对的次数


def fun2(strs):
    if len(strs) == 1:
        return strs[0]
        # 用来记录相同元素
    str = ""
    # 假设一个为abcdffg
    # 一个为abcd
    # 只需要循环到长度最短的末尾就行了
    first = strs[0]
    dier = strs[1]
    i = 0

    while i < len(first) and i < len(dier):
        if first[i] == dier[i]:
            str += first[i]
        else:
            break
        i += 1

    # 得到了他俩的公共前缀
    for i in range(2, len(strs)):
        item = strs[i]
        j = 0
        panduan = ""
        while j < len(item) and j < len(str):
            if str[j] == item[j]:
                panduan += str[j]
            else:
                if panduan == "":
                    return ""
                break
            j += 1
        str = panduan

    return str


#上面两种方法本质上都是暴力枚举



#他可以分解成更小的相似部分进行对比,所以可以使用
#递归
def fun3(strs):
    right=len(strs)-1
    if right==0:
        #说明只有一个元素
        return strs[0]
    mid=right//2
    leftStr,rightStr=fun3(strs[0:mid+1:]),fun3(strs[mid+1::])#这里为了防止有一个list为空所以这样切分
    #返回的结果为字符串,开始两个两个的找公共部分
    i=0
    while i<len(leftStr) and i<len(rightStr):
        if leftStr[i]!=rightStr[i]:
            #这里的问题是当前的i对应的值不相等所以后面就不用看了
            #前面的也没有问题所以直接return(和上面fun1()有点像的对比)
            return leftStr[0:i:]
        i+=1
    #循环结束还没有return的话就将最短的那个输出
    if len(leftStr)<len(rightStr):
        return leftStr
    else:
        return rightStr





if __name__ == '__main__':
    fun3(["flower","flow","flight"])

=============================================================================================================================================================================================================================================================================================

26.删除有序数组中的重复项

#首先给定的是有序的数组
#返回值是不重复元素组成数组的长度
#她会将其输出
#这也就是说,你只需要把重复的挪到后面就行了



#双指针的写法
def fun1(nums):
    #数组为空单独拿出来判断
    if nums==[]:
        return 0
    if len(nums)==1:
        #只有一个元素可以直接输出
        return 1
    quick,slow=1,1
    while quick<len(nums):
        #进行一下判断
        #数组是有序的,相同的元素会一窝一窝的聚集在一起
        #那么当,当前的quick和quick-1对应的元素不同时
        #你就到了另一窝
        if nums[quick]==nums[quick-1]:
            quick+=1
        else:
            nums[slow]=nums[quick]
            slow+=1
            quick+=1
    return slow

=============================================================================================================================================================================================================================================================================================

27. 移除元素

#



#这个好像是双指针优化
def fun1(nums,val):
    right=len(nums)-1
    cur=0
    while cur<=right:
        if nums[cur]!=val:
            #不相等
            cur+=1
        else:
            #相等
            #和right换位置
            nums[right],nums[cur]=nums[cur],nums[right]
            #因为你不知道换位置的那个元素,是否与val的值相等所以需要再进行一次判断
            right-=1
    return cur


#正常的双指针
#就是把应该输出的往前移动
#不应该输出的会随着'''应该输出'''的前移而不断后移
def fun2(nums,val):
    quick=0
    slow=0
    while quick<len(nums):
        if nums[quick]!=val:
            #将其前移
            nums[quick],nums[slow]=nums[slow],nums[quick]
            slow+=1
        quick+=1
    return slow


#其实fun2的代码nums[quick],nums[slow]=nums[slow],nums[quick]是不需要的
#因为你本身slow位置的元素从来都没有通过''slow下标''被比较过
#也就是说无论你那个位置放什么都行

def fun3(nums,val):
    quick=0
    slow=0
    while quick<len(nums):
        if nums[quick]!=val:
            #将其前移
            nums[slow]=nums[quick]
            slow+=1
        quick+=1
    return slow

=============================================================================================================================================================================================================================================================================================

28. 实现 strStr()
这里其实组好是使用KMP,但是我看了半天吐了

注意fun1和fun2都是暴力方法.但fun1逻辑上有些许问题,不够简洁,错误较多
fun2在80个测试点通过了78,后面的数据他大了.
注意fun2中采用双指针时,用 slow+quick.这使得slow不需要移动,只需要移动quick
方便了回溯(当匹配不成功回到原点+1)


def fun1(haystack,needle):
    if len(needle) == 0:
        return 0
    i = 0
    while i < len(needle):
        j = 0
        while j < len(haystack):
            if needle[i] != haystack[j]:
                # 如果是不等于有两种情况
                # 1.他是needle的首位元素:只需要j+1就行了
                # 2.他不是needle的首位元素:你是需要退回来
                if i == 0:
                    j += 1
                else:
                    i = 0
            else:
                if i == len(needle) - 1:
                    return j - i
                j += 1
                i += 1
        # 当你查完了一遍,i还等于0那么肯定是没对应的结果
        if i == 0:
            return -1
    return j - i


def fun2(haystack,needle):
    if len(needle) == 0:
        return 0


    slow=0
    while slow+len(needle)<=len(haystack):#(slow相当于匹配的起点)
        #这个变量使用了记录needle的位置
        quick = 0
        while quick<len(needle):
            #这里使用的是slow+quick所以在这个quick<len(needle)循环中slow始终不需要
            #进行数值的更改
            if needle[quick]==haystack[slow+quick]:
                #继续下一个字符的匹配
                quick+=1
            else:
                slow+=1
                break

        if quick==len(needle):
            return slow-1
    #如果没进来说明
    #needle的长度是大于haystack的
    return -1



=============================================================================================================================================================================================================================================================================================
35. 搜索插入位置

def fun1(nums,target):
    #这里使用left和right而不使用len(nums)的原因:
        #需要返回下标或插入元素.使用len(nums)就必须将<<列表进行切分,和加入变量保证索引的正确>>

    left=0
    right=len(nums)-1

    while left<right:
        mid = (left + right)//2
        zhongzhi = nums[mid]


        if(zhongzhi==target):
            #这里就可以直接返回了
                #题目<<提示>>中说:nums 为无重复元素的升序排列数组
            return mid
        elif(zhongzhi<target):
            #因为mid这个位置的值已经比较过了
            #所以赋值的时候为mid+1,不需要再比较
            left=mid+1
        elif(zhongzhi>target):
            right=mid-1

    #出循环是里面只有一个元素
    zhongzhi=nums[left]
    if (zhongzhi == target):
        # 这里就可以直接返回了
        # 题目<<提示>>中说:nums 为无重复元素的升序排列数组
        return left
    elif (zhongzhi < target):
        #这里有个问题,如果插入元素比所有元素都大
        #那么一定是插入到最后面,但是这不存在溢出数组的问题
        #所以可以直接插入


        #将元素插进来
        return left+1
    elif (zhongzhi > target):
        #这种情况需要注意一下
        #因为是插入到你的左边,所以存在当前位置为下标0的情况
        if left==0:
            return 0
        return left

=============================================================================================================================================================================================================================================================================================
动态规划
53. 最大子序和
fun1对应的方法是暴力破解,没有一点优化,每种情况都需要走一遍.一个203个测试点通过了201个,所以应该写的没问题,时间上需要优化

def fun1(nums):
    slow = 0
    max = 0
    panduan = True #这里是防止第一次的到的结果是个负数,或者所有结果都是负数,导致最后输出了0
    while slow < len(nums):
        quick = 0
        sum = 0
        while slow + quick < len(nums):
            sum += nums[slow + quick]
            if max < sum or panduan:
                #第一次不管怎么样都能进来,因为panduan为True
                #这时候:
                    #1.sum为负数,这是需要进行更改的
                    #2.sum为正数,本来就要进来更改
                    #3.sum为0,不影响
                
                #之后:
                    #只有sum的值大于max才能进来

                panduan = False
                max = sum
            quick += 1
        # while slow+quick<len(nums):循环结束,slow后移一下
        slow += 1
    return max


只有最后一个测试用例没通过,相对于最开始第一次的代码,更加的简洁,已读,高效
#由于有几天没有碰这一题了,所以先把暴力再写一遍
#基本思路就是设置一个最大值列表,用来记录每一个数对应的最大值

#而对于某个数的最大值,可以直接通过sum来记录
def fun2(nums):
    max_list=[]
    for i in range(len(nums)):
        sum=nums[i]
        max_sum=nums[i]
        for j in range(i+1,len(nums)):#从当前元素的后一位开始计算
            sum+=nums[j]  #加上当前元素,可能变大也可能变小,但都不能确定后面是否会有"超级super数"
            if sum>max_sum:
                #进行一个更换
                max_sum=sum
        #当循环结束所得到的max一定是在这个过程中的最大值
        max_list.append(max_sum)
    return max(max_list)


动态规划
#采用动态规划的方式写,可以看到有大量的重复性计算
#从后往前,用哈希表/字典进行记录(可以是对数据<<但细细想想nums中数据是有可能重复的>>,最好是对下标,下标具有唯一性)

#连续的最大子序和,所以假设当前位置为i
#你必须经过i+1,又可知i+1对应的最大值为x,所以i对应的最大值
#只能是i本身或i+x
def fun3(nums):
    n=len(nums)
    #创建列表,记录下标i处对应的值
    nums_copy=nums.copy()
    for i in range(n-1,0,-1):#这样的话,每一次的i需要-1
        nums_copy[i-1]=max(nums[i-1],nums[i-1]+nums_copy[i])

    return max(nums_copy)



动态规划,递归版本
def fun4(nums):
    if len(nums) == 1:
        return nums[0]  #因为在该种情况下返回值为数字,不是列表,所以直接拿出来单独讨论
    max_list=nums.copy()
    max_list=bijiao(max_list,0)
    return max(max_list)
def bijiao(max_list,i):
    if i==len(max_list)-1:#在遇见最后一位的时候停下来
        return max_list[i]
    max_list[i]=max(max_list[i],max_list[i]+bijiao(max_list,i+1))
    if i==0:
        return max_list
    return max_list[i]

=============================================================================================================================================================================================================================================================================================
动态规划
300. 最长递增子序列

这个通过了23个测试用例,因为碰到第22个那个测试用例太大了
def fun1(nums):
    #不做一点优化,每一种情况都走一遍
    #即使有重复的/以前走过的也继续走下去
    list1=[]
    for i in range(len(nums)):
        list1.append(bijiao(nums,i))
    return max(list1)
def bijiao(nums,i):
    max_len = 1
    for j in range(i + 1, len(nums)):
        if nums[i] < nums[j]:
            max_len=max(max_len,bijiao(nums,j)+1)
    return max_len



这个是模拟递归的非递归版本,想法和递归的思想没什么两样,就是用栈来模仿
主要问题就是代码写起来很难受

注意:错误代码
def fun2(nums):
    #这种方法是f1的迭代版本
    n=len(nums)
    max_list=[]
    for s in range(n):  #0<=s<n
        #创建一个栈
        #一个当前s元素对应一个栈
        stack=[]
        q=s
        i=1
        max_len=1     #设置当前元素对应的最长度初始值

        while q+i<n or stack:
            # 记录从当前q向后走过的长度,在栈中释放元素时
            len_ji = 0
            if nums[q+i]>nums[q]:
                #当前q入栈
                #q更换位置
                #i回归原来
                stack.append(q)
                q=q+i
                i=1
                len_ji+=1
                continue
            #如果没有进来
            i+=1 #向后继续判断

            #如果走到末尾,就判断栈中元素是否全部被释放
            if q+i==n-1:
                if stack:
                    #释放当前
                    q=stack.pop()+1
                    max_len=max(max_len,max_len+len_ji)
                    i=1



#假如说0,3,6,7是由03,6,7拼凑起来的
#3,6,7是由36,7拼凑起来的
#6,7
#----所以可以将列表从后往前遍历
#记忆化,把自己走过的路记住
def fun3(nums):
    #获取nums数组长度
    n=len(nums)

    #以数组长度开辟空间
    nums_copy=[1]*n
    for i in range(n,0,-1):
        for j in range(i,n): #因为nums_copy设置的默认值为1,所以nums数组尾部元素可以不进入循环,默认为1
            if nums[j]>nums[i-1]:
                #这样写是错误的原因在于
                #[3,7,101,18]若出现这种情况在运行的3时nums_copy的整体情况为
                #[1,2,1,1]  这时3,101,18均大于3,所以都会对3对应的最大长度
                #造成影响
                #nums_copy[i]=nums_copy[j]+1

                #所以我需要最大进行比较
                #就是当前该数(3)对应的最大长度和进行更改后的最大长度进行比较
                nums_copy[i-1]=max(nums_copy[i-1],nums_copy[j]+1)
    return max(nums_copy)


该方法是直接基于f1的改进通过字典,进行记录
def fun4(nums):
    dic={
    
    }
    max_list=[]
    for i in range(len(nums)):
        max_list.append(bijiao1(nums,i,dic))
    return max(max_list)

def bijiao1(nums,i,dic):
    if i in dic.keys():
        return dic[i]
    else:
        max_len=1
        for j in range(i+1,len(nums)):
            if nums[i]<nums[j]:
            	#在字典里面一定是最长长度
                dic[j]=bijiao1(nums,j,dic)
                max_len=max(max_len,dic[j]+1)
        return max_len

=============================================================================================================================================================================================================================================================================================
58. 最后一个单词的长度

#这个题目感觉玩不出来什么花样,就是进行简单的if,elif的判断

def fun1(s):
    sum = 0
    for i in reversed(range(len(s))):
        if s[i]!=" ":
            sum+=1
        elif s[i]==" " and sum!=0:#说明不是第一次进来
            break
    return sum

=============================================================================================================================================================================================================================================================================================

66. 加一

#第一种方法,很常见的,直接将所有数字拿出
#算出来对应的数,然后加1,在放进去(时间复杂度相对高,且并没有什么技术含量)



#第二种方法,将尾部进行加1操作后进行判断
#---如果为10将其上一位进行加1操作
#---如果该位为10,将其上一位进行加1操作
#......
#直到若为列表首位,1后为10,则需要进位

def fun2(digits):
    #首先将特殊情况999999排除
    n=len(digits)
    i=0
    stop=False
    while i<n and not stop:
        if digits[i]!=9:
            stop=True
    if stop:
        j=n-1
        while j>=0:
            if digits[j]+1==10:#该位不加1,让首位加1,该位变为0
            #试想一下如果digits[j]+x(x为任意数)不是一定为10怎么办
            #答案在最后
                digits[j]=0
                j-=1
                continue
            #执行若没进入则执行
            digits[j]+=1
            break
        return digits
    else:
        #说明全部都是9,那么直接变成1000...就好了
        list1=[0]*(n+1)
        list1[0]=1
        return list1

#(digits[j]+x)%10==1

=============================================================================================================================================================================================================================================================================================
67. 二进制求和

#第一想法二进制转化为10进制进行求和
def fun1(a,b):
    #将a转化为10进制
    a_len=len(a)-1
    b_len=len(b)-1
    a_shi=0
    b_shi=0
    for i in a:
        a_shi+=int(i)*2**a_len
        a_len-=1
    for i in b:
        b_shi+=int(i)*2**b_len
        b_len-=1
    sum_=a_shi+b_shi

    #再转化成二进制
    he=[]
    while sum_>0:
        he.append(sum_%2)
        sum_//=2
    sum_=0
    beishu=10
    for j in range(len(he),0,-1):
        sum_=sum_*beishu+he[j-1]

    return sum_

=============================================================================================================================================================================================================================================================================================
260. 只出现一次的数字 III

#这个题目有一种很巧妙的方法来求解
#下面就是代码
def fun1(nums):
    eor=0
    #首先是将数组遍历
    for i in nums:
        eor^=i
    #因为恰好有两个元素只出现一次
    #其他元素均出现偶数次(两次)
    #所以通过异或后
    #eor=a^b(a,b为不同数)
    #eor!=0

    #因为eor不为0,所以eor至少有一位(二进制存储)对应数为1
    #假设该位为"第10位"
        #第10位只有两种情况:0 or 1
        #所以可以通过第十位将所有数字进行分类  甲类和乙类
        #a,b一定一个为甲类,一个为乙类

    #找一个只有"第十位"为一的数 与 甲类做与运算,可以将属于甲类的全部提前出来,这样最后的结果一定为a or b


    right=eor&(~eor+1)  #这里可以将最右侧的1提取出来
                        #eor=101011001
                        #~eor=010100110
                        #~eor+1=010100111
                        #eor&(~eor+1)=000000001
    eor_a=0
    for i in nums:
        if(right&i==0):#说明该位置不为1
            eor_a^=i

    eor_b=eor^eor_a

    list1=[eor_a,eor_b]
    return list1

=============================================================================================================================================================================================================================================================================================
122. 买卖股票的最佳时机 II

#首先买入为-
#卖出为+

#这样的话这个题目就有点像最小子序和问题了
def fun1(prices):
    #开辟空间
    len_=len(prices)
    maichu=[0]*len_
    mairu=[0]*len_
    smdbg=[0]*len_
    for i in reversed(range(len(prices))):
        if(i==len_-1):
            maichu[i]=prices[i]
        elif(i==0):
            #只有买入情况
            mairu[i]=-prices[i]+max(max(maichu[i+1::]),0)
        else:
            #当前位置卖出可以得到的最大利润
            maichu[i]=prices[i]+max(max(mairu[i+1::]),0)
            #当前位置买入可以得到的最大利润
            mairu[i]=-prices[i]+max(max(maichu[i+1::]),0)
    return max(mairu)
print(fun1([1,2,3,4,5]))

=============================================================================================================================================================================================================================================================================================
217. 存在重复元素

#这个题目就是给一个数组,只要有一值存在两次即以上就可以输出了

#首先的思路就是遍历数组,字典记录还是很常规的操作o(n)的时间复杂度
def fun1(nums):
    dic={
    
    }
    for i in nums:
        if i in dic.keys():
            return True#因为说明i不是第一次出现了
        else:
            dic[i]=1
    return False

#排序也是可以的,归并排序,快速排序都行,很基础就不写了
#排序结束后相邻数两两比对

=============================================================================================================================================================================================================================================================================================
136. 只出现一次的数字
这个题目当时在学算法的时候是靠位运算写的,不得不说是一种很好的计算方法
以后可以往这里想一想,在这里推荐一下最近在学的算法课程
一周刷爆LeetCode,算法大神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教
讲的是很好的

#这个题目在我最近学习的马士兵算法里面已经做过了,不过现在复习复习还是挺好的



'''
神奇的位运算
    这里是二进制的存储方式
    1、与(&),按位与运算符:参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0
        同时为真,结果为真
    2、或(|),按位或运算符:只要对应的两个二进位有一个为1时,结果位就为1
        同时为假,结果为假
    3、异或(^),按位异或运算符:当两对应的二进位相异时,结果为1
        不同为1,相同为0
        0与任何数异或结果都为该数本身
    4、取反(~),按位取反运算符:对数据的每个二进制位取反,即把1变为0,把0变为1
        按位取反

    5、左位移(<<),运算数的各二进位全部左移若干位,由<<右边的数字指定了移动的位数,高位丢弃,低位补0
    6、右位移(>>),把‘>>’左边的运算数的各二进制位全部右移若干位,>>右边的数字指定了移动的位数
        这两个好像相当于乘以2,除以2,不过记不太清了
'''


#第一种方法使用"异或"运算符,因为一个数出现一次,其他数都出现两次,完全相同的数异或结果为0
def fun1(nums):
    eor=0
    for i in nums:
        eor^=i
    return eor

#第二种方法
#依旧是遍历数组
#依旧是哈希表
def fun2(nums):
    dic={
    
    }
    for i in nums:
        dic[i]=dic.get(i,0)+1#这个代码一定是先执行右边如果有就返回对应value,如果没有就返回0
    #这样的到的结果你也不知道谁是1
    #所以你可以将字典.items()转化成列表查找,不过感觉有些麻烦,突然想到了一种更好的方法

def fun3(nums):
    dic={
    
    }
    for i in nums:
        dic[i]=dic.get(i,0)+1
        if(dic[i]==2):
            dic.pop(i)
    for i in dic.keys():
        return i

=============================================================================================================================================================================================================================================================================================
350. 两个数组的交集 II

'''
    题目要求:
        给定两个数组,编写一个函数来计算他们的交集
        注意:
            这里是交集,是指只要nums1中出现该元素,且nums2中也出现该元素,则可说该元素存在于交集中
            并且输出的列表中每个元素出现的次数,等同于该元素在两个列表中出现的最小次数
'''

'''
    第一种方法,遍历大法+哈希表
        1.遍历nums1,将结果记录在哈希表(haxi1)当中
        2.遍历nums2,并查找haxi1中是否有,有则删除添加到输出列表中
'''

def fun1(nums1,nums2):#这种方法是我所喜爱的方法哈哈哈
    ls=[]
    dic={
    
    }
    for i in nums1:
        dic[i]=dic.get(i,0)+1
    #这样的话就得到了存储了所有的数据的字典
    for j in nums2:
        if j in dic.keys():
            if dic[j]==1:#说明现在还有一个值,直接删除掉
                dic.pop(j)
                ls.append(j)
            else:
                #只能是大于1
                dic[j]-=1
                ls.append(j)
    return ls


'''
    第二种方法:
        想到了一种很蠢的方法,排序+双指针的遍历
        这样的话nlogn+mlogm+遍历哈哈哈
        不了不了
'''


'''
    第三种方法:
        可以直接双指针吗??????
        两个数组本身都是无序的
        这就是说你只有暴力,把每种情况走一遍
        因为你不知道什么时候p_nums1移动,什么时候p_nums2移动
        
        所以这种方法可以说是暴力枚举哈哈哈
        比第二种还蠢,想偷懒,不可能
'''

=============================================================================================================================================================================================================================================================================================
283. 移动零

'''
    题目要求:
        给定一个数组nums,编写一个函数将所有的0移动到数组的末尾,同时保持非0元素的相对顺序
'''

'''
    方法一:
        遇到一个删除一个,最后一起加到最后面
        #这样的画每次的删除操作都是o(n)最后汇总虽然也是o(n)但是太慢了感觉
'''

def fun1(nums):
    sum1 = 0  # 用来计数的
    i = 0
    n = len(nums)
    while (i < n - sum1):  # 这里不能给len(nums),因为nums的长度在减少这就是:len(nums)<=>n-sum1
        if (nums[i] == 0):
            nums.pop(i)
            sum1 += 1
        else:
            i += 1
    for i in range(sum1):
        nums.append(0)


'''
    第二种方法
        开辟空间,用来存放非0数,后面再添加为0数
        这样的话会快一些,但需要消耗空间
        并且这个题目好像不想要你这样搞
    说明:
        必须在原数组上操作,不能拷贝额外的数组。
        尽量减少操作次数。
'''


'''
    那我们想一想怎么才能不额外开辟空间,同时时间复杂度还低
    换位置可以吗?
        试想一下,我现在的位置上对应的是个0,我可不可以循环找到第一个非0数(i)和他换位置,如果这样i与其他的相对元素位置不变
        但是0现在还是不在最后面?????
        哈哈哈我去看答案了,我发现就是这样,不过他用的是双指针,我是循环
        当然直接双指针更好
'''


'''
    改进写法,循环+单指针
        1.假设当前位置为0,循环找到第一个非0数(i)和其换位置
        2.直到指针溢出
    需要说明的是这种写法并不好,他只是一种思路
    我就是因为遍历次数过多才抛弃的
'''
def fun2(nums):
    for i in range(len(nums)):
        if nums[i]==0:
            for j in range(i+1,len(nums)):
                if nums[j]!=0:
                    nums[i],nums[j]=nums[j],nums[i]
                    break
                    #交换位置结束循环就好,如果为0,先啥都不干吧


'''
    上面的单指针+循环主要的问题是走了太多的重复路,对于每一个元素都什么都不管的遍历
    而采用双指针可以有效的避免这个问题
    基本思路:
        来两个指针一个慢,一个快
        快的用来找,慢的用来换
        在这个过程中有两种情况:
            [q]=0, q+1
            [q]!=0 交换q+1,s+1
        假设有一数组[0,7,0,0,9,3,5]
        1.s=0,[q]=7 交换[7,0,0,0,9,3,5] 此时s=1,q=2
        2.都为[q]=0,只改变q,此时q=4
        3.[q]=9,交换[7,9,0,0,0,3,5],此时s=2,q=5
        4.交换
        5.交换
        ...
        在这个过程中观察可以看到,s始终在已经排序的最后一位,等待着q找到!=的数和他交换
        所以整个过程始终是有序的,如果一直没有碰到0就是自己和自己交换
        
'''
def fun3(nums):
    s=q=0
    n=len(nums)
    while q<n:
        if nums[q]!=0:
            nums[s],nums[q]=nums[q],nums[s]
            s+=1
        q+=1

=============================================================================================================================================================================================================================================================================================
1. 两数之和

'''
    梦的开始,两数之和,人生第一道leetcode,今天他又回来了
    要求:
        给定一个整数数组nums,和一个整数目标值target,找出两个目标值(其和为target)返回对应下标
    看到这个题直接两种解放上来,双指针和哈希表
'''


'''
    解法1:字典+遍历
        这种方法就是记录,下标及其对应的值,因为下标唯一,且数组nums中可能存在重复元素,所以使用下标做键
        元组做值.不这样不好,由于下标做键,你需要遍历键值对,感觉很浪费,并且题目要求的只是返回一组结果
        所以我们还是元素做键,下标做值.
        我又感觉每一次查找很麻烦,不知道到底那个好,反正我写的是元素做键,下标做值
'''

def fun1(nums,target):
    dic={
    
    }
    for i in range(len(nums)):
        key=nums[i]
        for j in dic.keys():
            if j+key==target:
                return i,dic[j]#如果循环正常结束,则说明没有找到
        if key not in dic.keys():#不在则创建
            dic[key]=i

#看了一下答案,发下最好的写法就是哈希表
#然后我发现他是这样写的,查找target-i是否在哈希表中
def fun4(nums,target):
    dic = {
    
    }
    for i in range(len(nums)):
        key = nums[i]
        if target - key in dic.keys():#字典的查找是常数级操作
            return i, dic[target - key]
        # 能走到这说明没找到
        dic[key] = i  # 无论有没有key在哈希表中,直接赋值准没错

'''
    双指针懒得写了,有点暴力的感觉,准确来说就是暴力.不知道能不能优化一下
    在时间复杂度上落后太多
'''

def fun2(nums,target):
    pass



'''
    可不可以先排序,再二分????
    因为大于目标值的是可以排除掉的选项
    那么我先通过二分,找到合适的区间
    再开始查找???
    
    有个问题,记录最开始的下标
    
    
    突然又想到,我都已经排序了
    我直接拿target-当前数得到需要的数,然后二分查找?
    这不是非常完美
    
    那么重点来了,如何记录下标这个玩意
    我不知道,一会看答案有没有记录,我就直接查找好吧,得到这两个数之后
    再在未排序数组中直接查找这两个数对应的下标
    他不香吗??????????
'''

def fun3(nums,target):
    nums1=nums.copy()#复制一个
    nums.sort()#排序,偷个懒直接用人家的,毕竟我最近刚过完排序,这个代码的核心也是二分
    for i in range(len(nums1)):
        j=target-nums1[i]#如果要达到target需要j
        xiabiao=erfen(nums,j)
        if xiabiao:#这里是说这个j在nums中是否存在,存在
            #如果存在进来
            for c in range(len(nums1)):
                if nums1[c]==j:
                    if i!=c:#确定不是同一元素
                        return i,c
def erfen(nums,item):
    l=0
    r=len(nums)-1
    while l<=r:
        mid=(l+r)//2
        if nums[mid]<item:
            l=mid+1
        elif nums[mid]==item:
            return True
        else:
            r=mid-1
    return False

print(fun3([3,2,4],6))



=============================================================================================================================================================================================================================================================================================
36. 有效的数独
这个题目没有做出来,存在一定的问题
今天给他做出来了10.11
注意python中创建多维数组

#这个题目是二维数组
#可以说是二维数组的查找
#找不到重复元素就是正确的
'''
    数字 1-9 在每一行只能出现一次。
    数字 1-9 在每一列只能出现一次。
    数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
    只会出现重复和刚刚好两种情况

    注意:
        我只需要验证已经填入的数字是否是有效的,而不需要在意怎么解
'''


'''
    方法一(暴力循环+字典):
        直接暴力,这时很简单的思路假如说遍历一次是n,一共需要遍历3次,每一次遍历过程中
        只需要一个字典进行记录,再说了9*9的二维数组还是很小的 
'''

'''
    今天是10月11日,我又一次做这个题,很快的就做出来了,但是很明显,做麻烦了
    还记得上次做时判断行和列都很简单,唯独一个格子一个格子搞不出来
    今天我就这个样子想的
        用当前行(i)//3和当前列(j)//3
        可以得到:
            0,0  0,1  0,2
            1,0  1,1  1,2
            2,0  2,1  2,2
        这样就可以以:
            通过i//3与j//3的大小进行分组
'''
def fun1(board):
    ge_all_list1=[[],[],[]]#对角线
    ge_all_list2=[[],[],[]]#对角线上方
    ge_all_list3=[[],[],[]]#对角线下方
    for i in range(9):      #总的遍历次数
        hang_list=board[i]#第几行,对应的列表
        dic_hang={
    
    }#字典进行记录
        for j in range(9):
            if hang_list[j]!='.':
                #说明是数字,进行一个查找,有直接报错
                if hang_list[j] in dic_hang:#这就是查在不在字典里面
                    return False
                else:
                    dic_hang[hang_list[j]]=0#这里只要有就行了
                #这里还需要把格子元素放进去
                if i//3==j//3:#这样可以证明是对角线
                    ge_all_list1[i//3].append(hang_list[j])
                elif i//3<j//3:#如果为空所以可以放元素
                    ge_all_list2[i//3+j//3-1].append(hang_list[j])
                elif i//3>j//3:
                    ge_all_list3[i//3+j//3-1].append(hang_list[j])

    '''
        搞列就好了
    '''
    for i in range(9):
        dic_lie={
    
    }
        for j in range(9):
            #board[j][i],这样写,只动列不动行
            item=board[j][i]
            if item!='.':
                if item in dic_lie:
                    return False
                else:
                    dic_lie[item]=0

    #看格子:
    for i in range(3):#里面所具有的列表个数
        dic_lie1= {
    
    }
        dic_lie2= {
    
    }
        dic_lie3= {
    
    }
        item1=ge_all_list1[i]
        item2=ge_all_list2[i]
        item3=ge_all_list3[i]
        for j in range(len(item1)):
            if item1[j] in dic_lie1:
                return False
            dic_lie1[item1[j]]=0
        for j in range(len(item2)):
            if item2[j] in dic_lie2:
                return False
            dic_lie2[item2[j]]=0
        for j in range(len(item3)):
            if item3[j] in dic_lie3:
                return False
            dic_lie3[item3[j]] = 0
    return True


print(fun1([["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]))


'''
    看一下答案是怎么写的,答案只写了一个循环,但是思想没什么区别
    都是行看一遍,列看一遍,格子里面的看一遍
'''

'''
    注意看一格一格是这样的:
        假设i是行,j是列,会有i//3,j//3
            0,0  0,1  0,2
            1,0  1,1  1,2
            2,0  2,1  2,2
    这样可以开辟一个三维数组,长这个样子
        gezi=int[3][3][9]
        这样可以以i//3和j//3进行划分
    
'''
def fun2(board):
    hang=[[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0]]
    #lie=[[0]*9]*9,我最开是这样开辟数组的,但是有一个问题,对一个列表的更改,会影响其他
    #元素,我猜测是因为,他们其实是指向投以列表的内存地址,本质上是一个元素
    lie=[[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],
          [0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0]]
    gezi=[[[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]],
           [[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]],
           [[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]]]
    for i in range(9):
        for j in range(9):
            item=board[i][j]
            if item!='.':
                ind=eval(item)-1
                hang[i][ind]+=1#hang
                lie[j][ind]+=1
                gezi[i//3][j//3][ind]+=1
                if hang[i][ind]>1 or lie[j][ind]>1 or gezi[i//3][j//3][ind]>1:
                    return False
    return True


print(fun2([["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]))

ls=[[0]*9]*9#确实是指向同一个内存地址,所以牵一发而动全身
print(ls)#[[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], 
# [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], 
# [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]]
for i in ls:
    print(id(i))
    # 2352051450176
    # 2352051450176
    # 2352051450176
    # 2352051450176
    # 2352051450176
    # 2352051450176
    # 2352051450176
    # 2352051450176
    # 2352051450176
    

=============================================================================================================================================================================================================================================================================================

#这玩意和数组中的第一个数字一样的hash表就能做

'''
    方法一:
        循环+hash表
'''
def fun1(s):
    dic={
    
    }#哈希表
    for i in range(len(s)):
        if s[i] in dic:#[i]已经在字典当中
            #可以进行删除操作
            #dic.pop(s[i]) 这里不能删除,如果该元素出现了奇数次那么会导致第三次出现的时候被误替了
            dic[s[i]]=-1#索引不可能出现-1
        else:
            #不在字典中
            dic[s[i]]=i#让他进去
    #检查一下字典
    for i in dic:
        if dic[i]!=-1:
            return dic[i]#一般元素都是看似尾部的添加
    return -1

'''
    方法二:
        队列,队列本身具有先进先出的性质,在每次入对的时候进行一个判断,看元素的首位是否等同于
        入对元素,如果是进行出队操作.这样的话,遍历一遍字符串得到的队列的首位就是首个不重复元素
'''
'''
	写的有问题
'''
def fun2(s):
    #开辟队列空间
    duilie=[-1]*len(s)
    duilie[0]=[s[0],0]
    #记录出现多次但未删除元素
    dic={
    
    }
    #当前首位
    shouwei=0
    for i in range(1,len(s)):
        if s[i]==s[shouwei][0]:
            pass
print(fun2("aabb"))



=============================================================================================================================================================================================================================================================================================
344. 反转字符串

#题目要求原地反转(不使用额外空间)
#题目本身的难度并不高所以思路很简单

'''
    方法1:
        取中值使得
        i和n-i不断的调换位置
'''

def fun1(s):
    n=len(s)-1
    mid=n//2
    i=0
    while i<=mid:
        s[i],s[n-i]=s[n-i],s[i]
        i+=1
    print(s)
fun1(["H","a","n","n","a","h"])

=============================================================================================================================================================================================================================================================================================
7. 整数反转

'''
    首先直接转化为字符串,反转一下是非常简单的,也是非常愚蠢的,因为在大量使用别人的代码
'''

'''
    根据数学:
        不断的除以10取余数,是非常方便的
'''

def fun1(x):
    t=10
    sum1=0
    a=abs(x)
    while a>0:
        sum1=sum1*t+a%10
        a=a//10
    if not -2**31<=sum1<=2**31-1:
        return 0
    if x<0:
        return -sum1
    return sum1
print(fun1(123))

242. 有效的字母异位词

=============================================================================================================================================================================================================================================================================================

'''
    字母异位词:
        若s和t中每个字符出现的次数相同,则称s和t互为字母异位词
    从这个概念中不难看出:
        --你必须遍历完s和t,且每一次遍历都需要进行保存
        --在遍历结束后需要进行比较
        --如果长度不同,那么不可能字母异位词*****
'''

'''
    方法一:遍历+哈希表
'''
def fun1(s,t):
    cur=0#当前下标
    len_s=len(s)
    len_t=len(t)
    #首先判断长度是否相同
    if len_s!=len_t:
        return False#不可能了,我俩
    s_dic={
    
    }
    t_dic={
    
    }
    while cur<len_s:
        s_dic[s[cur]]=s_dic.get(s[cur],0)+1
        t_dic[t[cur]]=t_dic.get(t[cur],0)+1
        cur+=1
    for t_key in t_dic:#循环拿出键
        if t_key not in s_dic or t_dic[t_key]!=s_dic[t_key]:
            #只要t的键不在s字典里面,或者该元素,在两个表中出现的次数不同则结束了
            return False
    return True
print(fun1("anagram","nagaram"))


'''
    第二种方法:
        排序,对字符串进行排序(通过unicode编码转化为数字进行比较)
        对于字母异位词,排序后,s和t是两个一模一样得字符串
'''
'''
	这个排序直接用的人家的,要我写的话只能是先转化为对应得数字
	排序,再转回来,当然因为字符本身是可以比较的,所以不转也行
'''
def fun2(s,t):
    if len(s)!=len(t):
        return False
    s=list(s)
    t=list(t)#需要注意一下,字符串是不可以字节排序的,需要转化为列表
    s.sort()
    t.sort()
    for i in range(len(s)):
        if s[i]!=t[i]:
            return False
    return True
fun2("anagram","nagaram")

猜你喜欢

转载自blog.csdn.net/qq_53183608/article/details/119676941