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

总结算法中可以前后处理的方法实例
世间有好坏,算法逻辑也有前后‘因果’,我们可以从数组中看出有第一项和最后一项。
以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))
posted @ 2021-12-21 21:35  索匣  阅读(161)  评论(0编辑  收藏  举报