• 算法逻辑中的因果关系(持续更新)


    总结算法中可以前后处理的方法实例
    世间有好坏,算法逻辑也有前后‘因果’,我们可以从数组中看出有第一项和最后一项。
    以LQ26删除有序数组中的重复项举例
    可以把不相等的数字往前移动,这样最多会改变一个数据,如果没有重复数据存在的时候,那就是最前面的(索引0)数据。
    若有相同,那就不一样了;

    数组篇

    数据前后移动处理

    数据往前移动处理

    处理关键是慢指针的处理,他是确定相同数据的指标;有相同的数据他就停下来

      n = len(nums) # 有序数组才可使用快慢双指针排序
      fast = slow = 1
      while fast < n:  
          # 为什么不等于的数据往前移动      
          if nums[fast] != nums[fast-1]:
              nums[slow] = nums[fast]
              slow +=1 # 前后不同把他替换成功的慢指针加1
          fast+=1 # 快指针不停的走;这样会把原来的数据替换没,下方书上那个应该不改变原来元素
      return slow # (28 ms),这题到底返回啥,数字还是列表
    

    数据往后移动处理

      n = len(set(nums))
      i = 1
      while i < n: # 这是先求出n,在来对列表进行切片赋值
          if nums[i] == nums[i-1]: # 等于的数据往后移动
              temp = nums[i] # 果然报错
              nums[i: len(nums) -1] = nums[i+1:] # list index out of range
              nums[-1] = temp
              continue
          else:
              i += 1
      return n #  (5988 ms)用了len(set())所以很慢,但数组中的数据没改变,还是输出了不重复的数据
    
    

    LQ[217] 存在重复元素

    
    ## 第三种方法使用sort排序后
            nums.sort()
            # 正的
            # for i in range(len(nums)-1):
            # 倒的
            for i in range(len(nums)-1,0,-1):
                # if nums[i] == nums[i+1]: # 正的
                if nums[i] == nums[i-1]:
                    return True
            return False #  (100 ms) (96 ms)
    

    哈希算法

    判断比较数组中元素的个数,通用(使用哈希表)

    # nums是数组
    a = {}
            for num in nums:
                # 这里的条件是不在字典中加1,就是有点逆序的想法
                if num not in a: # 这里加一个a.keys()时间复杂度就超时了
                    a[num] = 1 # 创建哈希表元素
                else:
                    # 这里是关键,通过get获取指定元素值来加1
                    a[num] = a.get(num) + 1
    

    这是a的键就是元素的值,a的值就是元素的个数,直接从a中的键读取元素,也可以使用a.keys(),a.values()提取出所有的键,所有的值。
    提取出来的是个列表,再在提取出来的基础上进行操作,就一目了然

    另外一种哈希方式

    采用字典的keys和continue来完成,在某种程度上和上面的if,else运行的语句相反

    d = {}
    for i in s:
        if i in d.keys(): # 第二次出现时,要捕获并结束这次循环
            d[i] += 1
            continue
        d[i]=1
    

    该方法可以求两数组中相同的元素并提取,然后不同的元素保留(并没有去重的话相同元素有多个)

     ## 两数组长短不一求交集
            # 交集肯定在短的中
            if len(nums1) > len(nums2):
                nums1,nums2 = nums2,nums1
            # 反正就是nums1这个名字集合最小
            a = []
            for i in set(nums2):
                for j in set(nums1):
                    if i == j:
                        a.append(i)
                        nums1.remove(j)
            return a # (76 ms)
    
    

    将列表中的元素全部转化为字符串

    使用生成器即可;digits是个数字的列表

    a = ("%s"%i for i in digits)
    

    数字位上的操作

    LQ66 加一
    数字分个位,十位,百位依次递增;需要考虑的特殊情况都为9

    
    # 正宗倒序,对每个元素进行处理
    digits.reverse() # 列表元素倒序,修改自身
    flag  = True # 设定一个为9的标志
    for i in range(len(digits)):
        if flag: # 只有都为9的似乎后才需要flag等于true
            # 这是处理999都是9的情况
            if digits[i] == 9:
                digits[i] = 0
            else:
                digits[i] += 1
                flag = False # 只要对最后一位元素加1,其他各尽一位
    if digits[-1] == 0:
        digits.append(1)
    # 处理完加法后,在倒序回去
    digits.reverse()
    return digits #  (24 ms)
    

    双指针法经常作用的点

    上面的数据 数据往前后移动处理也使用了双指针
    指针指向的位置:pos
    指针的移动特点:fea

    LQ[283] 移动零

    pos:0,0
    fea:left元素遇到0,整体后移,right一直后移,遇到非0;left,right均后移(right在这里就是个条件统计所有元素的条件)

    left = 0
    right = 0 # 以索引位置来做比较
    while right < len(nums) -1: # len(nums)做了个统计很耗时
        if nums[left] == 0 :
            nums[left:-1] = nums[left+1:]
            nums[-1] = 0
            right +=1
        else: # 就算为left不为0,right也要走完
            left +=1
            right +=1
    return nums #  (532 ms)
    

    突然的想法

    是不是有些用统计长度的内置函数不使用,换成其他方式解决算法题木。

    得到索引

    LQ[1] 两数之和
    替换变量值的思想

    
    d = {}
      for i in range(len(nums)): #  (12 ms)
          b = target - nums[i]
          if nums[i] in d: # 处理相同元素的,直接用in d,不要加.keys(),.values(),字典太仁慈了
              return [d[nums[i]],i]
          else:
              # 反正在不在都得往字典中加入,键值分别为被减数和索引;通过减数与被减数得到索引
              d[b] = i # 值/别人的索引
    

    字符串篇

    统计与乘2除2相关的操作

    为什么要乘2,为什么要除2
    LQ344

    # 第3种方法,除2来界定for循环边界
      length = len(s)
      if length < 2:
          return 
      for i in range(length//2):
          # 这其实比较形象,显得出要循环迭代的次数;有点废脑子
          # 以中间为分界线,对称呼唤
          s[i],s[length-1-i]=s[length-i-1],s[i] #  (64 ms)
    

    验证回文串

    LQ[125] 验证回文串,这里有些回文串包含除字母外的其他字符。
    最好使用while循环,right > left,一个递增,一个递减,就算是中间相同也没关系会退出
    并不能使用,这里的字符有些是并不是字符,那么次数就不管用,所以这是错误的

    # 可以不用while,这里的次数可以算出来
    for i in range(len(s)//2): # 但还是尽量少用len(s)//2,就算是对称的
      # 最好使用right > left,一个递增,一个递减,就算是中间相同也没关系会推出
      if not s[left].isalnum():
          left +=1
          continue
      if not s[right].isalnum():
          right -=1
          continue
      if s[left]==s[right]:
          left +=1
          right -=1
      else:
          return False
    return True # (492 ms) 我的解法都好慢啊
    

    上面的是错误的,要用除2判断次数,必须要对字符串进行一定的处理

    if len(s) < 2:
        return True # 小于2只有1个字符串就为回文串
    sList = []
    s = s.lower() # 全部处理为小写
    for word in s:
        if word.isalnum():
            sList.append(word) # 创建新空间提取字母
    n = len(sList) // 2
    # 这里要处理下用倒序,取出n为奇数偶数个干扰
    if sList[:n] == sList[::-1][:n]:
        return True
    else:
        return False # 就是太浪费空间 #  (32 ms)
    

    while解法(双指针),可以不用预先处理字符串,

    s = s.lower()
    left = 0
    right = len(s)-1
    while right - left > 0:
      # 从左边开始不是一个有效字符,左加1
      if not s[left].isalnum():
          left +=1
          continue
      if not s[right].isalnum():
          right -=1
          continue
      # 判断是否为字符串,不是字符串时候执行
      if s[left]==s[right]:
          left +=1
          right -=1
          print('左边索引为',left)
          print('右边索引为',right)
          # 两者索引靠近,就会等于0
      else:
          return False
    

    递归与while循环

    基本上使用while循环的函数都可以用递归来做
    递归的结束条件就是while循环的结束条件,递归地进行下去的条件就是while循环中的自增自减,递归中对数据的操作和while循环中一样
    两者都有相同的结束条件和进行的条件,对数据的操作也相同
    LQ344

    while循环

    def test(s):
        l= 0
        r = len(s)-1
        while True:
            s[l],s[r] = s[r],s[l] # while语句中进行的操作
            # while得以进行的条件
            l +=1
            r -=1
            if l >= r: # 结束条件
                return s # (40 ms)
    

    递归

    class jj:
      def test(self,s)
          return self.digui(s,0,len(s)-1) # (40 ms)
      def digui(self,s,left,right):
          # 结束条件
          if left>=right: # 当前面所有超过后面索引返回
              return s
          # 反正都是一次替换2个,不过这个方法不一样
          # 递归进行的条件
          self.digui(s,left+1,right-1)# 把双指针的加剪边成了递归拆解
          # 递归时进行的操作;基本上任何可以使用while进行的函数都可以使用递归
          s[left],s[right] = s[right],s[left] # 这是赋值
    

    链表

    链表的构成可以参考实现链表的完整代码

    反转链表

    一般链表的题目只会给个head,链表的特性是用next指针连接起来,而next指针又为下一个元素
    这里主要注意对链表反转之前的操作,要进行判断是否为空链表,是否为单链表,对双指针的right和left进行处理,right为前进的链表头,把他的下一个指向指为自身(这是反转的关键),left的next开始为None

    
    # 两个指针的用法
    if head == None:
        return None
    left = right = head
    if right.next == None:
        return head
    else:
        # 这是对平常的处理,赋值
        # right的下一个指向指向right
        # lext的下一个指向指向None
        right = right.next
        left.next = None
    
    while right != None:
        # 玩的就是个变量的赋值
        head = right # 变量h被赋值r,不断地把r变为头节点
        right = right.next # r要继续导出下一个节点,把变量赋值给r
        head.next = left  # h的next指向到l
        left = head # h的值又给到l,不断把头节点变为左节点
    return head #  (12 ms)
    

    创建26个小写字母为键,值为0的字典

    words = [chr(i) for i in range(97,123)]
    values = [0] *26
    words_dict = dict(zip(words,values))
    
    努力拼搏吧,不要害怕,不要去规划,不要迷茫。但你一定要在路上一直的走下去,尽管可能停滞不前,但也要走。
  • 相关阅读:
    Spring——框架
    Spring——简介
    系统常识——系统碎片
    php5.3.3以上重启php-fpm的方法
    linux后端跑redis
    php的post
    微信场景二维码
    phantomjs和selenium模拟登陆qq空间
    python登录知乎
    python的request抓https的警告问题
  • 原文地址:https://www.cnblogs.com/wkhzwmr/p/15717294.html
Copyright © 2020-2023  润新知