python学习记录:算法练习(1)

"""1、问题描述
    将0元素全部移到数组后边,非0元素相对位置不变
"""
class removezero:
    def __init__(self,nums):
        self.num = nums
    def rmzeros(self):
        nums = self.num
        flag = 0 #这里flag用于记录0的位置
        for index in range(len(nums)):
            if nums[index] != 0: 
            #显然,当没有0元素时,index与flag指示的位置是一致的;而一旦出现0元素,此时index与flag的移动就不一致了,
            #index会继续后移,而flag则停止后移,此时即记录了0的位置;若下一个元素为0,则只记录第一个位置的0,
            #若下一个为非0,则交换位置,第一个0移到了0后第一个非0元素的位置;同时,flag更新到第二个0的位置;
                nums[flag] = nums[index]
                if index != flag:
                    nums[index] = 0
                flag = flag + 1
        return nums
    def __repr__(self):
        return repr(self.num)
    def rm(self):
        nums = self.num
        flag = 0 
        for index in range(len(nums)):
            #这里更直接,与上述方法的区别在于:这里不区别flag与index相等的情况,直接全部交换,而上述则是在不相等时0与非0交换;
            if nums[index] != 0:
                nums[index],nums[flag] = nums[flag],nums[index]
                flag = flag + 1
        return nums
    
nu = removezero([1,0,3,2,0,0,8,9,0])
print(nu)
print(nu.rm())
print(nu.rmzeros())
nzero = removezero([])
print(nzero)
print(nzero.rm())
[1, 0, 3, 2, 0, 0, 8, 9, 0]
[1, 3, 2, 8, 9, 0, 0, 0, 0]
[1, 3, 2, 8, 9, 0, 0, 0, 0]
[]
[]
"""
2、二叉搜索树
(1)定义:
    指一颗空树,或者一颗具有下列性质的数:
    //左子树上的所有节点均小于其根节点的值;
    //右子树上的所有节点均大于其根节点的值;
    //其每个节点的左右子树也都是二叉搜索树
(2)二叉树的遍历方法:
    分为三种:
    //前序遍历,亦即按照根节点—左子树—右子树的顺序进行遍历访问;
    //中序遍历,亦即按照左—根—右的顺序进行遍历访问,也就是从小到大的顺序升序排列;
    //后序遍历,亦即按照左—右—根的顺序进行遍历访问;
"""
#定义树的节点,对于排列好的二叉树,n个节点也就意味着n个treenode对象,每个节点对象中的left与right都指向不同的节点值
#这是定义的一种递归调用的方法
class treenode:
    def __init__(self,value):
        self.value = value
        self.left = None
        self.right = None
    #进行前序遍历的代码
    def preorder(self,root):
        if root:
            self.traverse_path.append(root.value)
            self.preorder(root.left)
            self.preorder(root.right)
    
    #进行中序遍历的代码
    def inorder(self,root):
        if root:
            self.inorder(root.left)
            self.traverse_path.append(root.value)
            self.inorder(root.right)
    #进行后续遍历的代码
    def postorder(self,root):
        if root:
            self.postorder(self.left)
            self.postorder(self.right)
            self.traverse_path.append(root.value)
    
#下面是一种实现前序遍历的迭代方法,而中序和后序只需要将right、left压入栈stack的顺序进行改变即可
class Solution:
    def preorderTraversal(self, root):
        result = []
        #人工维护一个栈,先将根节点存入
        stack = [(0,root)]
        #执行下列的迭代遍历,直到stack为空
        while stack:
            #栈的特点:后进先出,对应可二叉树访问中访问到最后叶子节点才决定哪个返回的特点
            #同时list列表的append操作是将元素追加在最后,而pop操作是将最后的元素弹出,正好实现了“栈”的操作
            flag,node = stack.pop()
            #如果节点为None,则表示访问到了某个叶子的左、右节点,此时不做处理
            if node is None:
                continue
            #这里的flag为标识变量,用以标识是否该节点被访问过
            #如果已经访问过,即1,则表示子节点已处理完,直接返回该节点的值即可;
            #如果未访问过,即0,则表示已该节点为根节点的子树没有被处理遍历过,需要对其左右子树进行继续访问
            #访问遍历的规则即前序、中序、后序的规则,需要注意的是先append的是后遍历的,因此要注意顺序
            if flag == 0:
                #这里为前序遍历,根—左—右的顺序,而将各节点存入栈的顺序则要反过来;
                stack.append((0,node.right))
                stack.append((0,node.left))
                stack.append((1,node))
            else:
                result.append(node.val)
        return result
#递归的写法,这里只要顺序不同即可实现不同的遍历
class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        if root:
            return [root.val]+self.preorderTraversal(root.left)+self.preorderTraversal(root.right)
        else:
            return []

[1, 0, 4, 0, 2, 0, 0, 6, 8]
"""
3、验证二叉树的写法
"""
##个人一开始学习到的,以迭代的方式进行中序遍历,遍历的同时进行元素的大小判断
#7月1日写
class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        #最直接的方法,全部遍历一遍,每个节点都作判断
        #采用中序遍历
        if root is None:
            return True
        reslist = []
        resdict = {}
        stack = [(0,root)]
        while stack:
            flag,node = stack.pop()
            if node is None:
                continue
            if flag == 0:
                stack.append((0,node.right))
                stack.append((1,node))
                stack.append((0,node.left))
            else:
                if node.val in resdict:
                    return False
                else:
                    resdict[node.val] = node.val
                    reslist.append(node.val)
        if sorted(reslist) == reslist:
            return True
        else:
            return False
        
#7月2日对其改进了对list结果的排序,其时间复杂度较高
class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        #最直接的方法,全部遍历一遍,每个节点都作判断
        #采用中序遍历
        if root is None:
            return True
        reslist = []
        resdict = {}
        stack = [(0,root)]
        while stack:
            flag,node = stack.pop()
            if node is None:
                continue
            if flag == 0:
                stack.append((0,node.right))
                stack.append((1,node))
                stack.append((0,node.left))
            else:
                #7月2日更新,其实这里也大可不必进行字典操作,只要列表中最后的元素节点值大于等于当前判断节点即可判Falese
                #if node.val in resdict:
                #    return False
                #el
                if len(reslist)!=0:
                    if reslist[-1] >= node.val:
                        return False
                resdict[node.val] = node.val
                #reslist.append(node.val)
        #其实,通过了while的判断而没有提前return,就基本确定没有问题
        #这里大可不必做判断,直接return True就好
        if list(resdict) == reslist:
            return True
###别人写的时间复杂度比较低的方法,采用了递归调用的方法进行中序遍历
class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        self.prev=None
        def inorder(root):
            if not root:return True
            if not inorder(root.left):return False
            if self.prev is not None and root.val<=self.prev:return False
            self.prev=root.val
            return inorder(root.right)
        return inorder(root)
[1, 4, 2, 6, 8, 0, 0, 0, 0]
[1, 4, 2, 6, 8, 0, 0, 0, 0]
"""
4、验证回文串——要求:只比对数字和字母,忽略其余特殊字符,另外空串也是回文串
两大解题思路:
(1)双指针法,逐个进行比较;
(2)借助Python中的特殊API函数,如正则化表达式、如列表生成式等
"""
###个人第一次解法:
class Solution:
    def isPalindrome(self, s: str) -> bool:
        #全部转化为小写字母
        s = s.lower()
        length = len(s)
        #定义末尾指针
        j = length - 1
        #无论是否为数字和字母,长度为0和1,均为回文
        if(length <= 1):
            return True
        #进行指针遍历
        for i in range(length):
            #如果左侧指针指向的为非数字字母,此时分两种情况——如果已经与右指针碰到了,说明中间全为非数字字母,就为TRUE;反之,就指针右移
            if(not s[i].isalnum()):
                if(i == j):
                    return True
                else:
                    continue
            #如果为字母,就看右指针的指向是否为数字字母,一直移动到指向字母为止;
            #显然,在左指针已经指向字母的情况下,右指针左移的极限就是左指针当前的位置,
            #指向的字母相等时,如果右指针等于左指针或者在左指针右侧一位,则为回文;反之,需要移动右指针,进行下一循环判断;
            #指向的字母不相等,直接判定非回文
            else:
                while(not s[j].isalnum()):
                    j -= 1
                if s[i] == s[j]:
                    if j > i + 1:
                        j -=1
                        continue
                    else:
                        return True
                else:
                    return False
##借助python接口函数的快速解法——列表推导式
class Solution:
    def isPalindrome(self, s: str) -> bool:
        s = s.lower()
        s_copy = [e for e in s if e.isalnum()]
        return s_copy == s_copy[::-1]
##借助python的正则化表示库re——sub(pattern,repl,string)
class Solution:
    def isPalindrome(self, s: str) -> bool:
        s_copy = re.sub("[^a-zA-Z0-9]","",s)
        s_copy = s_copy.lower()
        return s_copy == s_copy[::-1]
##官网上比较靠前的代码形式——使用join函数将列表推导式筛选后的结果重新连成字符串
class Solution:
    def isPalindrome(self, s: str) -> bool:
        s_copy = "".join(e.lower() for e in s if e.isalnum())
        return s_copy == s_copy[::-1]

##官网上比较靠前的代码形式——使用filter()函数配合join()函数,本质上与上述没有区别,但filter比推导式更快
class Solution:
    def isPalindrome(self, s: str) -> bool:
        s_copy = "".join(filter(str.isalnum,s)).lower()
        return s_copy == s_copy[::-1]

###双指针法的参考写法
"""
(1)设置一个头指针和一个尾指针
(2)头指针从头开始找,找到第一个数字或字符
(3)尾指针从尾开始找,找到最后一个数字或字符
(4)比较两者是否相同,不同就return False
(5)相同则两指针往中间靠(关注指针的位置更新)
(6)当 i > j 的时候,return True
"""
class Solution:
    def isPalindrome(self, s: str) -> bool:
        i, j = 0, len(s) - 1
        while i < j:
            while i < len(s) and not s[i].isalnum():
                i += 1
            while j > -1 and not s[j].isalnum():
                j -= 1
            if i > j:
                return True
            if s[i].upper() != s[j].upper():
                return False
            else:
                i += 1
                j -= 1
        return True
"""
5、区间最大和求解
题目要求:
    给定n个正整数组成的数列以及一个数M,要求在这n个正整数中寻找一个区间[i,j](即数列的第i个元素到第j个元素),这个区间和要是小于M的最大和;
    如果有多个这样的区间和(即小于M的最大和区间不唯一),则返回i值最小的区间
这个题的目的,是逐步推导优化时间复杂度从O(N^3)、O(N^2)、O(NlogN)、O(N);
示例:
    输入:5 10
        2 3 4 5 6
    输出:1 3 9
    #即表示数组5个数字,M = 10,第1个到第3个的区间和最大,和为9
"""
# 从控制台键盘输入尝试
#x = input("please input numbers:").split(sep = " ")
#x_int = [int(e) for e in x if len(e) != 0]
n = 5
M = 10 
nums = [2,3,4,5,6]
### (1)最直接,最暴力的解法——逐个遍历,这样会有两重循环,使用了python中的求和sum函数,相当于逐个遍历一遍元素,又一重循环
def interval_Sum_max(n,M,nums):
    #n为数组长度,M为指定的最大数字,nums为数组
    interval_i = 0
    interval_j = 0
    interval_sum = 0
    temp_sum = 0
    for i in range(n):
        for j in range(i + 1, n + 1):
            temp_sum = sum(nums[i:j])
            print("sum of [{i},{j}] = {sum}".format(i = i+1, j = j,sum =temp_sum))
            #这里调试需要注意的是,为什么要j-1,因为num[i,j]的求和,实际上是对第i个到第(j-1)个元素进行求和的
            #同时,注意到第二层循环是i+1 到 n+1,也是考虑到了进行nums[i,j]切片时,如果j按照最后一个元素的位置索引(n-1)的话,是无法对最后一个
            #元素求和的
            if temp_sum > interval_sum and temp_sum <= M:
                #这里为什么要给i+1,而给j保持原样呢?——这就是因为python索引从0开始,所以区间左端的索引加1才对呀实际的第几个元素
                #而j保持原样,则就是因为pyhon进行切片时[i,j]实际上只提取到索引的j-1元素,而恰恰对应了实际索引的第j个元素,所以不用变
                #这里对区间内符合要求的求和进行不断更新
                interval_i = i + 1
                interval_j = j
                interval_sum = temp_sum
    return [interval_i,interval_j,interval_sum]
interval_Sum_max(n,M,nums)

###(2)
sum of [1,1] = 2
sum of [1,2] = 5
sum of [1,3] = 9
sum of [1,4] = 14
sum of [1,5] = 20
sum of [2,2] = 3
sum of [2,3] = 7
sum of [2,4] = 12
sum of [2,5] = 18
sum of [3,3] = 4
sum of [3,4] = 9
sum of [3,5] = 15
sum of [4,4] = 5
sum of [4,5] = 11
sum of [5,5] = 6





[1, 3, 9]
"""
6、变位词问题——比较两个单词的组成字母是否是相同的,包括数量
"""
def comp_words(str1,str2):
    len_of_str1 = len(str1)
    len_of_str2 = len(str2)
    dict_str1,dict_str2 = {},{}
    if(len_of_str1 != len_of_str2):
        return False
    else:
        for i in range(len_of_str1):
            if str1[i] not in dict_str1:
                dict_str1[str1[i]] = 1
            else:
                dict_str1[str1[i]] += 1
            if str2[i] not in dict_str2:
                dict_str2[str2[i]] = 1
            else:
                dict_str2[str2[i]] +=1
    if dict_str1 == dict_str2:
        return True
    else:
        return False
comp_words(str1 = "sdshsjsksl",str2 = "sdshsjskl")
False
len(x[1])
2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值