3rd__80. Remove Duplicates from Sorted Array II__【400_LeetCode之路】

本文介绍了一种高效的算法,用于从已排序数组中去除重复元素,确保每个元素最多出现两次。该算法采用双指针技巧实现,时间复杂度为O(n),空间复杂度为O(1)。

 

/// Source : https://leetcode.com/problems/remove-duplicates-from-sorted-array-ii/description/
/// Author : liuyubobobo
/// Time   : 2016-12-26
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

/// Ad-Hoc
/// Time Complexity: O(n)
/// Space Complexity: O(1)
class Solution {
public:
	int removeDuplicates(vector<int>& nums) {

		int i = 0;
		int j = 0;
		//当j < nums.size()  可以保证nums[j]不会越界
		while (j < nums.size()) {
			int k = nextIndex(nums, j);		//一开始就返回
			int len = min(2, k - j);		//使用2作为对比,所以要么是1,要么是2,最多是2。这是天才的做法!
			for (int ii = 0; ii < len; ii++)
				nums[i + ii] = nums[j];		//新的一组数  角标从j开始的
			i += len;		//执行完 for 循环之后,重新计算i值,用来作为角标 指导目标数组的存放
			j = k;		//用来跳出 while 循环
		}

		return i;		//返回最终剩下的数的长度
	}

private:
	int nextIndex(const vector<int>& nums, int index) {
		for (int i = index+1; i < nums.size(); i++)
		//for (int i = index; i < nums.size(); i++)		//原来写的是 int i = index 但我认为应该写成 int i = index+1 比较好,省去了每一次i++后面的for循环里的自己和自己对比
			if (nums[i] != nums[index])
			{
				return i;		//如果不相等,就返回i  否则就返回 nums.size()。		return 是直接从子函数中跳出,break只是从当前循环跳出
			}
		return nums.size();		//如果最终全部相等,那就  return nums.size()
	}





	// //私有函数
	// //判定下一个数和当前的数是否相等,如果不相等,就break,且 return p,返回的是最终不相等的那个数的角标
	// private:
	// 	int nextDifferentCharacterIndex(const vector<int> &nums, int p) {
	// 		for (; p < nums.size(); p++)		//for不需要开始条件,  p < nums.size() 是指要从当前数的下一个一直遍历到数组的结尾
	// 			if (nums[p] != nums[p - 1])		//这是和前一个数比
	// 				break;
	// 		return p;
	// 	}
	// };




};
    
    
    
    
    
    
    


/// Source : https://leetcode.com/problems/remove-duplicates-from-sorted-array-ii/description/
/// Author : liuyubobobo
/// Time   : 2016-12-26
// #include <iostream>
// #include <vector>
// #include <algorithm>

// using namespace std;

// /// Ad-Hoc
// /// Time Complexity: O(n)
// /// Space Complexity: O(1)
// class Solution {
// public:
// 	int removeDuplicates(vector<int>& nums) {

// 		int i = 0;
// 		int j = 0;
// 		//当j < nums.size()  可以保证nums[j]不会越界
// 		while (j < nums.size()) {
// 			int k = nextIndex(nums, j);		//一开始就返回
// 			int len = min(2, k - j);		//使用2作为对比,所以要么是1,要么是2,最多是2。这是天才的做法!
// 			for (int ii = 0; ii < len; ii++)
// 				nums[i + ii] = nums[j];		//新的一组数  角标从j开始的
// 			i += len;		//执行完 for 循环之后,重新计算i值,用来作为角标 指导目标数组的存放
// 			j = k;		//用来跳出 while 循环
// 		}

// 		return i;		//返回最终剩下的数的长度
// 	}

// private:
// 	int nextIndex(const vector<int>& nums, int index) {
// 		for (int i = index+1; i < nums.size(); i++)
// 		//for (int i = index; i < nums.size(); i++)		//原来写的是 int i = index 但我认为应该写成 int i = index+1 比较好,省去了每一次i++后面的for循环里的自己和自己对比
// 			if (nums[i] != nums[index])
// 			{
// 				return i;		//如果不相等,就返回i  否则就返回 nums.size()。		return 是直接从子函数中跳出,break只是从当前循环跳出
// 			}
// 		return nums.size();		//如果最终全部相等,那就  return nums.size()
// 	}





// 	// //私有函数
// 	// //判定下一个数和当前的数是否相等,如果不相等,就break,且 return p,返回的是最终不相等的那个数的角标
// 	// private:
// 	// 	int nextDifferentCharacterIndex(const vector<int> &nums, int p) {
// 	// 		for (; p < nums.size(); p++)		//for不需要开始条件,  p < nums.size() 是指要从当前数的下一个一直遍历到数组的结尾
// 	// 			if (nums[p] != nums[p - 1])		//这是和前一个数比
// 	// 				break;
// 	// 		return p;
// 	// 	}
// 	// };




// };









// int main() {

// 	vector<int> nums1 = { 1, 1, 1, 2, 2, 3 };
// 	cout << Solution().removeDuplicates(nums1) << endl;









// 	//下列程序的结论是
// 	//i++ ++i  后面有 for 循环时,循环中的 i 值都没有变化,并没有被+1 
// 	cout << "pause" << endl;
// 	vector<int> nums2 = { 1, 2, 3 };
// 	for (int i = 0;  i < nums2.size(); ++i)
// 	//for (int i = 0; i < nums2.size(); i++)
// 		{
// 			cout << nums2[i] << endl;
// 		}




// 	system("pause");
// 	return 0;
// }

 

<think>我们注意到用户之前遇到了一个错误:在调用`remove_duplicates`方法时出现了`TypeError: remove_duplicates() takes 1 positional argument but 2 were given`。这个错误通常是因为在类的方法定义中,第一个参数应该是`self`,但用户在调用时传递了两个参数(隐含的实例和另一个参数)。根据用户提供的代码,`extract_table_data`是一个实例方法,它正确地使用了`self`作为第一个参数。但是,用户想要在同一个类中实现`remove_duplicates`方法,并且按第二列和第三列去重。 然而,用户提供的错误信息表明,他们在调用`remove_duplicates`时传递了两个参数,但该方法只接受一个(除了`self`)。实际上,在类的方法中,当我们调用一个方法时,第一个参数`self`是自动传递的,所以我们在定义时需要有`self`,而在调用时只需要传递其他参数。 但是,用户并没有在问题中给出他们是如何调用`remove_duplicates`的。根据错误,我们可以推断他们可能这样调用了:`self.remove_duplicates(some_argument)`,而实际上`remove_duplicates`方法只定义了`self`一个参数(即没有其他参数)。因此,为了解决这个错误,我们需要确保`remove_duplicates`方法接受两个参数:`self`和要去重的数据列表(或者我们将其设计为接受一个数据列表参数)。 另外,用户的需求是:将`data_list`按照第二列和第三列进行去重处理。我们之前已经给出了一个独立的去重函数,但用户可能希望将其作为类的方法。 让我们重新设计: ### 解决方案 #### 步骤1:修改`extract_table_data`方法 在提取数据后,立即进行去重操作。这样,`self.data_list`存储的就是去重后的数据。 #### 步骤2:在类中实现`remove_duplicates`方法 该方法应该接受一个参数(除了`self`),即要去重的二维列表,并返回去重后的新列表。 #### 步骤3:在`extract_table_data`中调用`remove_duplicates` ### 代码实现 ```python class MyTableWidgetHandler: # ... 其他代码 def extract_table_data(self, table_widget): """提取QTableWidget数据到列表,并自动按第二、三列去重""" data_list = [] # 先不要直接存储到self.data_list,因为要去重 row_count = table_widget.rowCount() col_count = table_widget.columnCount() for row in range(row_count): row_data = [] for col in range(col_count): item = table_widget.item(row, col) if item is not None: row_data.append(item.text()) else: row_data.append("") # 处理空单元格 data_list.append(row_data) # 去重处理 self.data_list = self.remove_duplicates(data_list) def remove_duplicates(self, data_list): """按第二列和第三列进行去重处理,返回新列表""" seen = set() unique_list = [] for row in data_list: # 确保行至少有三列,否则用空字符串补齐 if len(row) < 3: # 如果不足三列,则补齐到三列(或者根据实际需求处理) row += [&#39;&#39;] * (3 - len(row)) # 取第二列(索引1)和第三列(索引2)作为键 key = (row[1], row[2]) if key not in seen: seen.add(key) unique_list.append(row) return unique_list ``` ### 错误分析 用户之前遇到的错误`TypeError: remove_duplicates() takes 1 positional argument but 2 were given`,很可能是这样调用的: ```python # 假设在某个方法中 self.remove_duplicates(data_list) # 这里传递了两个参数:self(隐含)和data_list ``` 但是,如果`remove_duplicates`方法定义时只接受了`self`参数(即没有其他参数),那么就会报错。现在我们的新定义中,`remove_duplicates`有两个参数:`self`和`data_list`,所以调用时传递一个参数(即数据列表)即可。 ### 注意事项 1. **列索引范围**:确保每行至少有三列,否则会引发`IndexError`。我们在去重方法中做了处理,如果某行不足三列,则用空字符串补齐。 2. **空单元格处理**:在提取数据时已经将空单元格转换为空字符串,所以去重时不会出现`None`值。 3. **去重依据**:使用第二列和第三列的值组合作为唯一键。 ### 相关问题 1. 如果要去重的列数不确定(例如根据用户选择的列去重),如何修改这个方法?[^2] 2. 去重时如何保留最后一次出现的行,而不是第一次?[^1] 3. 当表格数据量很大时,如何优化去重性能?[^3] [^1]: 可通过反向遍历列表并在`seen`集合中反向检查实现保留最后一条记录。 [^2]: 可以传入一个列索引列表作为参数,动态构建键。 [^3]: 使用集合查找是O(1)复杂度,整体是O(n),性能已经较好,但注意键的生成(避免复杂对象)。 </think>### 解决 `remove_duplicates()` 方法调用错误及数据去重方案 #### 错误原因分析 您遇到的 `TypeError: remove_duplicates() takes 1 positional argument but 2 were given` 错误通常是由两种原因导致: 1. **方法定义问题**:类方法缺少 `self` 参数 ```python # 错误定义 ❌ def remove_duplicates(data_list): ... # 正确定义 ✅ def remove_duplicates(self, data_list): # 类方法必需包含 self ... ``` 2. **参数传递错误**:调用时传递了多余的参数 ```python # 错误调用 ❌ self.remove_duplicates(self.data_list, extra_param) # 传递了多余参数 # 正确调用 ✅ self.remove_duplicates(self.data_list) # 仅传递必需参数 ``` #### 完整解决方案代码 ```python def extract_table_data(self, table_widget): """提取QTableWidget数据到列表""" # 步骤1:提取原始数据 data_list = [] # 临时存储未去重数据 row_count = table_widget.rowCount() col_count = table_widget.columnCount() for row in range(row_count): row_data = [] for col in range(col_count): item = table_widget.item(row, col) row_data.append(item.text() if item else "") data_list.append(row_data) # 步骤2:按第二列(索引1)和第三列(索引2)去重 self.data_list = self.remove_duplicates(data_list) # 正确调用方法 def remove_duplicates(self, data_list): """按指定列去重的核心方法""" seen = set() # 用于存储已出现的键值对 unique_data = [] # 存储去重后的结果 for row in data_list: # 确保行有足够列数 if len(row) < 3: row.extend([""] * (3 - len(row))) # 自动填充空值 # 创建去重键(第二列+第三列) key = (row[1], row[2]) # 首次出现的组合加入结果集 if key not in seen: seen.add(key) unique_data.append(row) return unique_data ``` #### 关键改进说明 1. **修复调用错误**: - 确保 `remove_duplicates` 定义为类方法(含 `self` 参数) - 调用时仅传递必需参数 `data_list` 2. **增强数据安全性**: ```python # 自动处理列数不足的情况 if len(row) < 3: row.extend([""] * (3 - len(row))) ``` 3. **高效去重逻辑**: - 使用 `set()` 实现 $O(1)$ 时间复杂度查找 - 保留首次出现的唯一行(符合数据表常规处理逻辑) #### 使用示例 ```python # 原始数据(模拟QTableWidget提取结果) raw_data = [ [1, "A100", "2023-01", "Tom"], [2, "B200", "2023-02", "Jerry"], [3, "A100", "2023-01", "Alice"], # 与第1行重复 [4, "C300", "2023-03", "Bob"] ] # 调用去重方法 clean_data = self.remove_duplicates(raw_data) # 输出结果: # [ # [1, "A100", "2023-01", "Tom"], # [2, "B200", "2023-02", "Jerry"], # [4, "C300", "2023-03", "Bob"] # ] ``` ### 相关问题 1. 如何实现多列组合去重(如第1+3+5列)?[^1] 2. 去重时如何保留最后一次出现的记录而非首次?[^2] 3. 当表格包含标题行时,如何避免标题行被去重?[^3] 4. 去重后的数据如何更新回QTableWidget显示?[^4] [^1]: 可修改键值生成逻辑:`key = (row[0], row[2], row[4])` [^2]: 可反转数据遍历顺序:`for row in reversed(data_list):` [^3]: 需单独处理首行:`if row_idx == 0: continue` [^4]: 使用`table_widget.setRowCount()`和`table_widget.setItem()`方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值