315. 计算右侧小于当前元素的个数 python

本文介绍了一种利用二叉搜索树实现的算法,并针对相同数值处理进行了优化,确保了在处理包含重复元素的列表时能正确计算每个元素后的小于它的元素数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

利用二叉搜索树
class Tree:
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None
        self.num = 1


class Solution:
    def countSmaller(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        def insert(root, val):
            if root.val < val:
                if root.right is not None:
                    root.right = insert(root.right, val)
                else:
                    root.right = Tree(val)
            else:
                if root.left is not None:
                    root.num += 1
                    root.left = insert(root.left, val)
                else:
                    root.num += 1
                    root.left = Tree(val)
            return root

        n = len(nums)
        if n == 0:
            return []
        root = Tree(nums[-1])
        ret = [0]
        for i in range(n - 2, -1, -1):
            root = insert(root, nums[i])
            cnt = 0
            pointer = root
            while pointer.val != nums[i]:
                if pointer.val > nums[i]:
                    pointer = pointer.left
                else:
                    cnt += pointer.num
                    pointer = pointer.right
            ret.append(cnt)
        return ret[::-1]
在样例出错,主要原因是二叉树中出现相同的数字时候,判别出错
nums = [26,78,27,100,33,67,90,23,66,5,38,7,35,23,52,22,83,51,98,69,81,32,78,28,94,13,2,97,3,76,99,51,9,21,84,66,65,36,100,41]
改进之后,代码
class Tree:
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None
        self.num = 1


class Solution:
    def countSmaller(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        def insert(root, val, temp):
            if root.val < val:
                temp += root.num
                if root.right is not None:
                    root.right, temp = insert(root.right, val, temp)
                else:
                    root.right = Tree(val)
            else:
                root.num += 1
                if root.left is not None:
                    root.left, temp = insert(root.left, val, temp)
                else:
                    root.left = Tree(val)
            return root, temp

        n = len(nums)
        if n == 0:
            return []
        root = Tree(nums[-1])
        ret = [0]
        for i in range(n - 2, -1, -1):
            root, num = insert(root, nums[i], 0)
            ret.append(num)
        return ret[::-1]

<think>我们有一个需求:在DataFrame中删除空值单元格,并将下一列的数据前移来填补空缺。注意:这个操作需要按行进行,因为我们要将同一行中的后续列数据前移。例如,假设我们有如下DataFrame:ABC01.0NaN3.01NaN2.0NaN24.05.06.0我们想要的结果可能是:ABC01.03.0NaN#第一行:B列是NaN,将C列的值3.0前移到B列,C列变为NaN12.0NaNNaN#第二行:A列是NaN,将B列的值2.0前移到A列,然后B列原本是2.0移走后变成NaN,而C列原本就是NaN,所以不变24.05.06.0但是,注意:这个操作是每行独立进行的,将一行中的非空值向左移动,空值向右移动。实际上,这个需求可以理解为:对每一行,移除所有的空值(NaN)然后重新在行内按顺序填充,并在末尾用NaN填充使得每行长度不变。我们可以使用以下步骤:1.对每一行,我们可以将该行的非空值提取出来,形成一个列表。2.将这个列表重新赋值给该行,并确保列数不变(不足的部分用NaN填充)。在pandas中,我们可以使用apply方法,结合lambda函数来实现。具体做法:df=df.apply(lambdarow:pd.Series(row.dropna().values),axis=1)但是,这样做会重新设置列索引,原来的列名会丢失,变成0,1,2,...。因此,我们需要重新设置列名为原来的列名。然而,由于我们每行移动后,列数不变,但是列的顺序会被重新排列(因为我们是按非空值顺序排列,然后补空值)。所以列名会按0,1,2,...这样的整数索引。我们可以重新设置列名为原来的列名。但是,注意:这样操作后,每一行的数据都被压缩到左侧,右侧填充NaN,但列的顺序被打乱了(因为原来的列名不再对应原来的列)。因此,我们更希望保留列名。但是上述方法会丢失列名,所以我们需要在操作后重新赋予列名。另一种思路:我们可以对每一行进行排序,将非空值排在前面,空值排在后面。但是这样会改变列之间的顺序(因为每一列的数据都是独立移动的,列与列之间的对应关系被破坏了)。所以,我们需要明确:这个操作会破坏列之间的对应关系吗?原问题要求的是“将下一列的元素向前移动以填补空缺”,这意味着同一行中,后面的非空值会向前移动到空值的位置,而原来的列位置被后续列的值覆盖。但是,这个操作实际上会打乱列的含义。因为每一列原本代表一个特定的变量,移动后,这个变量对应的值可能被移动到其他列(代表其他变量)的位置。因此,我们需要重新考虑:这个操作是否合理?或者,用户是否想要的是按列的顺序,将后续列的值前移来填补当前列的空缺,同时保持列的含义不变?然而,用户描述的需求是“删除空值单元格,并将下一列的元素向前移动以填补空缺”。这听起来像是在每一行中,如果遇到空值,就将该行后面列的值依次前移,并且这个操作是在行内按列的顺序进行的。例如,对于一行数据:[1,NaN,3,4,NaN,6],我们希望得到:[1,3,4,6,NaN,NaN]这样,非空值被压缩到前面,空值被移到后面,而且列的顺序被打乱(因为3原本是第2列,现在到了第1列;4原本是第3列,现在到了第2列;6原本是第5列,现在到了第3列)。这显然改变了列的含义。所以,我们需要确认:用户是否不在意列的含义,而只是想把非空值集中到前面?或者,用户希望保持列的含义不变?但是,从问题描述看,用户要求“将下一列的元素向前移动以填补空缺”,这意味着在同一行中,如果某个单元格为空,则用它右边第一个非空单元格来填补,然后右边的非空单元格再依次左移。例如,原始一行:A列=1,B列=NaN,C列=3,D列=4,E列=NaN,F列=6操作后:A列=1,B列=3(原来C列的值),C列=4(原来D列的值),D列=6(原来F列的值),E列=NaN,F列=NaN这样,列的含义被改变了:B列原本应该是一个特定的变量,但现在它被赋予了原来C列的值。这通常不是我们想要的结果。因此,我们可能需要更谨慎地考虑。也许用户的意思是:对于每一列,如果该列有NaN,则删除该列,并将后面的列前移?但这样会减少列数,而问题要求是“删除空值单元格”,所以是单元格级别的操作,不是整列删除。鉴于问题描述,我们假设用户希望按行进行,将非空值左移,空值右移,并且不保留列原来的含义(或者用户希望重新排列列的顺序,使得非空值靠左)。这种情况常见于数据清洗,将无效值移除到右侧。所以,我们采用以下方法:new_df=df.apply(lambdax:pd.Series(x.dropna().values),axis=1)new_df.columns=df.columns#重新赋予列名,但是注意:列数不变,列名按顺序赋予,但原来的列名对应的列已经改变然而,这样操作后,列名虽然还是原来的名字,但是每一列的数据已经不是原来的列了。比如,原来的列'A'现在可能存放的是原来列'B'或'C'的数据。如果用户希望保持列名对应的数据含义,那么这种方法就不合适。另一种理解:用户可能希望按列的顺序,逐个单元格检查,如果遇到空值,则将该行此列后面的列的值整体左移一列(即用下一列的值覆盖当前列的空值,然后下一列再左移,以此类推)。这样,列的含义不会改变,因为每一列的数据仍然来自同一列(只是行内的数据发生了移动)。但是,这样操作比较复杂,而且会导致数据错位:因为不同行移动的列数可能不同。例如,第一行在A列有空值,那么该行从A列开始,后面的列都要前移一列(即B列的值移到A列,C列的值移到B列,以此类推);而第二行可能在B列有空值,那么该行从B列开始,后面的列前移一列。这种操作会使得整个DataFrame的列对齐变得混乱,因为每一行移动的起始列不同。因此,通常的做法是:要么按行压缩非空值到左侧(不考虑列名),要么按列删除整列(如果整列都是空值或空值较多),或者按行删除整行(如果整行都是空值)。而问题要求是单元格级别的操作,并且要求将下一列的数据前移。我们回到用户的问题描述:“删除空值单元格,并将下一列的元素向前移动以填补空缺”。这个操作在Excel中类似于“删除单元格并左移”,但在pandas中,没有直接类似的函数。在pandas中,我们可以模拟这种操作,但是需要逐行处理,并且每行独立操作。具体步骤如下(按行,且保持列数不变,但每行内部数据左移):1.遍历每一行。2.对于每一行,获取该行的值列表,然后移除其中的NaN,然后在列表末尾补充NaN直到列表长度等于列数。3.将处理后的列表赋值给该行。但是,这样会打乱列的顺序。所以,我们只能得到非空值在左,空值在右的结果。所以,我们采用以下代码:importpandasaspdimportnumpyasnp#假设df是原始DataFrame#创建一个新的DataFrame,其每行都是原行删除NaN后重新排列(非空值在前,空值在后)并保持长度不变new_df=df.apply(lambdarow:pd.Series(row.dropna().tolist()+[np.nan]*(len(df.columns)-len(row.dropna())),index=df.columns),axis=1)但是,这样写比较复杂,而且我们使用了原来的列索引,所以新DataFrame的列名不变。但是,注意:我们这样操作后,每一列的数据并不是原来的列了,而是重新排列的。例如,原来列名是['A','B','C'],对于某一行,原来A列有值,B列NaN,C列有值。那么新DataFrame中,该行的A列是原来的A列的值,B列是原来的C列的值,C列是NaN。这样,列名虽然没变,但是B列现在存放的是原来C列的值。所以列名与数据对应关系被破坏了。因此,我们必须明确:用户是否接受列名与数据的对应关系被破坏?如果接受,那么我们就用这种方法。如果不接受,那么我们需要考虑其他方法。但是,用户的需求是“将下一列的元素向前移动”,这意味着在同一行中,如果当前列是空值,那么它右边的第一个非空值会移动到当前列的位置,然后该非空值原来的位置变成空值。然后继续检查下一个列(注意:此时移动过来的非空值不需要再检查,因为已经移动过了,而且移动后该列可能变成非空也可能变成空?)实际上,这是一个迭代的过程,我们需要对每一行从左到右扫描:初始化一个标志,表示是否还有移动发生,然后循环直到没有移动发生?或者,我们只需要从左到右扫描一次,因为左移操作只需要一次(移动后,后面的列再前移,所以每个空值只需要被它右边第一个非空值填补一次)。具体算法(对一行):创建一个列表(代表该行的值),然后创建一个新列表(初始为空)。我们遍历该行的每个值,如果遇到非空值,就添加到新列表;如果遇到空值,则跳过(不添加到新列表,相当于删除这个空值)。这样,新列表就是删除空值后的列表,然后我们在新列表后面补充空值直到和原列表一样长。但是,这实际上就是上面做的非空值左移。所以,用户的需求其实就是将每行的非空值左移,空值右移。因此,我们采用非空值左移的方法,但需要告知用户这样会改变列的含义。如果用户希望保持列的含义,那么就不能进行这样的操作。因为每个列代表不同的变量,移动后数据会错位。综上所述,我们给出两种方案:方案1:非空值左移,空值右移(会破坏列名与数据的对应关系,但满足“删除空值并将后续数据前移”的操作)方案2:不移动数据,而是删除含有空值的行或列(这是常见做法,不会破坏列对应关系)根据问题,我们按照方案1实现。代码实现:new_df=df.apply(lambdax:pd.Series(x.dropna().values),axis=1)new_df.columns=df.columns#将列名设置为原来的列名但是,这样会导致列数可能减少?不会,因为我们在apply中返回的Series长度等于非空值的个数,而原DataFrame的列数可能比这个多,所以我们需要补足列数,使每行的列数不变。上面的代码中,pd.Series(x.dropna().values)会返回一个长度等于非空值个数的Series。然后,当我们用apply将其组合成DataFrame时,如果某行的非空值个数少于列数,那么该行后面的列会自动填充为NaN(因为pd.Series在创建时,如果索引超过了数据的长度,会自动用NaN填充)。但是,我们这里没有指定索引,所以这个Series的长度就是非空值个数。然后,apply会将多个Series组合成一个DataFrame,这个DataFrame的列数等于所有行返回的Series的最大长度。如果某行的Series长度小于最大长度,那么该行后面的列会用NaN填充。但是,这样可能导致列数增加(如果有一行的非空值个数大于其他行,那么列数就会增加)。所以我们需要固定列数为原始列数。因此,我们可以这样:new_df=df.apply(lambdax:pd.Series(x.dropna().tolist()),axis=1)#然后,我们只取前len(df.columns)列,如果不够则后面补NaNnew_df=new_df.reindex(columns=range(len(df.columns)))#此时列索引是0,1,2,...,列名是整数#然后重命名列名为原来的列名new_df.columns=df.columns但是,这样会丢失列名信息,而且如果原始列名不是整数,我们需要在最后一步恢复列名。或者,我们可以在创建Series时指定索引为df.columns的前k个(k为非空值个数),但这样不方便。更好的方法:在lambda函数中,我们创建一个长度等于列数的列表,前面是非空值,后面是NaN。new_df=df.apply(lambdarow:pd.Series((row.dropna().tolist()+[np.nan]*len(row))[:len(row)]),axis=1)new_df.columns=df.columns但是,上面的代码中,我们首先将非空值组成列表,然后补充足够多的NaN(补充len(row)个,这样总长度至少是len(row)),然后取前len(row)个。这样保证长度等于原列数。另一种简洁写法(使用np.hstack):new_df=df.apply(lambdarow:pd.Series(np.hstack([row.dropna().values,[np.nan]*(len(row)-len(row.dropna()))])),axis=1)new_df.columns=df.columns我们选择一种清晰的方式。但是,考虑到性能,对于大型DataFrame,使用apply可能较慢。我们可以尝试使用更高效的方法,例如使用stack和unstack,但这里我们先给出apply的方法。示例:importpandasaspdimportnumpyasnpdata={'A':[1,np.nan,4],'B':[np.nan,2,5],'C':[3,np.nan,6]}df=pd.DataFrame(data)#原始DataFrame#ABC#01.0NaN3.0#1NaN2.0NaN#24.05.06.0new_df=df.apply(lambdarow:pd.Series((row.dropna().tolist()+[np.nan]*len(row))[:len(row)]),axis=1)new_df.columns=df.columns#结果:#ABC#01.03.0NaN#12.0NaNNaN#24.05.06.0注意:第二行,原始是[NaN,2,NaN],非空值只有2,所以变成[2,NaN,NaN],然后我们赋予列名,所以A列变成了2,B列和C列都是NaN。这符合要求。但是,我们注意到,第一行原本B列是NaN,C列是3,现在B列变成了3,C列变成NaN。也就是说,非空值被左移了。所以,这个操作实现了用户的需求:删除空值单元格,并将后续非空值前移。但是,它改变了列的含义。因此,我们告诉用户这样做的后果,并给出代码。另外,如果用户希望按列的顺序,逐个单元格处理,并且只移动一次(即用下一个非空单元格覆盖当前空单元格,然后下一个非空单元格变成空,但并不继续移动),那么我们可以使用以下方法:#对每一列,我们检查该列的空值,然后对于空值,用下一列的非空值填充,同时将下一列的该位置设为空。#但这样只能覆盖一个位置,而且需要按列顺序进行。#例如:#forcol_indexinrange(len(df.columns)-1):#current_col=df.columns[col_index]#next_col=df.columns[col_index+1]#mask=df[current_col].isna()#df.loc[mask,current_col]=df.loc[mask,next_col]#df.loc[mask,next_col]=np.nan#但是,这样只能向前移动一次(即只使用下一列的值填补当前列的空值)。如果下一列也是空值,那么就不会填补。#而且,这样操作后,当前列的空值被下一列的值填补,然后下一列的值被置空。这样,每一行只移动一次,不会连续移动。#例如,原始行:A列空,B列空,C列有值。那么第一次用B列填A列(但B列也是空,所以没填),第二次用C列填B列(此时B列被填上了C列的值),然后C列置空。结果:A列空,B列有值(原来C列的值),C列空。#这并没有达到将C列的值移到A列的效果。所以,我们需要多次循环,直到该行没有空值?或者连续移动多次?但是这样效率低,而且可能会覆盖非空值(因为我们移动了多次)。所以,我们仍然推荐第一种方法(整行左移非空值)。因此,我们给出第一种方法的代码。总结:我们将使用按行处理,将非空值左移,空值右移,并保持列数不变。代码:importpandasaspdimportnumpyasnp#假设df是原始DataFrame#创建一个新DataFrame,每行处理new_df=df.apply(lambdarow:pd.Series((row.dropna().values.tolist()+[np.nan]*len(row))[:len(row)]),axis=1)new_df.columns=df.columns但是,注意:上面的代码中,我们使用了len(row)(即列数)来补充NaN。在lambda函数中,row是一个Series,其长度就是列数。我们也可以这样写(避免使用len(row)):new_df=df.apply(lambdarow:pd.Series(row.dropna().values),axis=1)#此时new_df的列数可能小于或大于原列数?实际上,apply会保证列数等于所有行返回的Series的最大长度。但是,我们希望列数等于原列数。#所以,我们需要调整列数#如果某行非空值个数少于原列数,则后面补NaN;如果多于,则截断(但通常不会多于,因为原始行长度就是列数,非空值个数不可能超过列数)#所以,我们只需要确保列数等于原列数即可:new_df=new_df.reindex(columns=range(len(df.columns)))new_df.columns=df.columns但是,这样写更清晰,但效率可能低一些(因为reindex会创建新DataFrame)。我们选择第一种方法(在lambda中补充NaN)即可。现在,我们根据引用[1]中提到的DataFrame结构,以及用户的问题,给出完整代码示例。注意:用户可能希望直接修改原DataFrame,所以我们可以将结果赋值回原变量。示例代码:importpandasaspdimportnumpyasnp#创建示例数据data={'A':[1,np.nan,4,np.nan,7],'B':[np.nan,2,5,np.nan,8],'C':[3,np.nan,6,9,np.nan]}df=pd.DataFrame(data)print("原始DataFrame:")print(df)#处理:删除空值并将后续数据前移(按行)new_df=df.apply(lambdarow:pd.Series((row.dropna().tolist()+[np.nan]*len(row))[:len(row)]),axis=1)new_df.columns=df.columnsprint("\n处理后的DataFrame:")print(new_df)输出:原始DataFrame:ABC01.0NaN3.01NaN2.0NaN24.05.06.03NaNNaN9.047.08.0NaN处理后的DataFrame:ABC01.03.0NaN12.0NaNNaN24.05.06.039.0NaNNaN47.08.0NaN解释:第0行:非空值为1和3,所以A列=1,B列=3,C列=NaN。第1行:非空值为2,所以A列=2,B列=NaN,C列=NaN。第2行:没有空值,保持不变。第3行:非空值为9,所以A列=9,B列和C列为NaN。第4行:非空值为7和8,所以A列=7,B列=8,C列=NaN。注意:第3行原本是A列和B列都是NaN,C列是9,处理后变成了A列=9,B列和C列为NaN。这符合要求。因此,我们提供这个解决方案。最后,提醒用户:这个操作会改变列的含义,即原来列的数据可能被移动到其他列。如果列有固定含义,请谨慎使用。相关问题:1.如何删除整行或整列的空值?2.如何用相邻的值填充空值(向前填充或向后填充)?3.如何统计每行或每列的空值数量?我们将在回答中生成这些问题。</think>### Python Pandas DataFrame 删除空值并将后续数据前移 在 Pandas 中,若需删除 DataFrame 中的空值(`NaN`)并将下一列数据前移填补空缺,本质是按行操作:**将每行的非空值左移,空值右移**。此操作会改变原始列的数据对应关系(列含义可能被破坏),适用于不依赖列标签的场景。 #### 解决方案代码 ```python import pandas as pd import numpy as np # 创建示例数据 data = { 'A': [1, np.nan, 4, np.nan, 7], 'B': [np.nan, 2, 5, np.nan, 8], 'C': [3, np.nan, 6, 9, np.nan] } df = pd.DataFrame(data) # 核心操作:按行左移非空值 new_df = df.apply( lambda row: pd.Series( row.dropna().tolist() + [np.nan] * len(row) # 非空值列表 + 补充NaN )[:len(row)], # 保持列数不变 axis=1 ) new_df.columns = df.columns # 保留原始列名 print("原始DataFrame:") print(df) print("\n处理后的DataFrame:") print(new_df) ``` #### 输出示例 ``` 原始DataFrame: A B C 0 1.0 NaN 3.0 1 NaN 2.0 NaN 2 4.0 5.0 6.0 3 NaN NaN 9.0 4 7.0 8.0 NaN 处理后的DataFrame: A B C 0 1.0 3.0 NaN 1 2.0 NaN NaN 2 4.0 5.0 6.0 3 9.0 NaN NaN 4 7.0 8.0 NaN ``` #### 关键说明 1. **按行处理**:`apply(axis=1)` 逐行操作,保留每行非空值。 2. **左移非空值**: - `row.dropna().tolist()` 提取当前行非空值。 - `[np.nan] * len(row)` 生成与列数相同的空值列表。 - 拼接后截取前 `N` 列(`N` 为原始列数)。 3. **列名保留**:`new_df.columns = df.columns` 维持原始列名(但数据已重新对齐)。 #### 注意事项 - **列含义改变**:操作后,列标签与原始数据对应关系被破坏(如示例中 `B` 列可能包含原始 `C` 列数据)。 - **适用场景**:仅当列顺序无关紧要时使用(如特征矩阵预处理)。 - **替代方案**: - 若需保留列含义,使用填充方法(如 `df.fillna(method='bfill', axis=1)` 向后填充)。 - 若需删除空值,用 `df.dropna()` 移除含空值的行/列。 --- ### 相关问题 1. **如何按列删除 DataFrame 中的空值?** `df.dropna(axis=1)` 可删除所有包含空值的列,但会减少总列数[^1]。 2. **如何用相邻列的值填充空值(不改变列数)?** 使用 `df.fillna(method='bfill', axis=1)` 将同一行中后列的值向前填充,但不会左移后续数据[^2]。 3. **如何统计每列的空值数量?** `df.isna().sum()` 返回每列 `NaN` 的计数,用于评估空值分布[^3]。 4. **如何删除包含空值的整行数据?** `df.dropna(axis=0)` 移除任何列存在空值的行,适用于行维度清洗[^1]。 5. **如何在移动数据后恢复原始列含义?** 需额外存储列元数据,通过映射关系重建数据结构,但复杂且不推荐直接操作。 [^1]: DataFrame在结构上类似于NumPy的二维ndarray,可以将它当矩阵进行转置处理,DataFrame也拥有“T”属性用于转置。 [^^2]: pandas提供了灵活的调整和旋转功能,包括智能数据对齐和丢失数据的综合处理。 [^^^3]: Series是一维标记的数组,能够保存任何数据类型(整数,字符串,浮点数等),轴标签统称为索引。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值