这里写目录标题
数组
1. 从排序的数组中删除重复的内容【remove-duplicates-from-sorted-array】
给出一个排序后的数组,在原地删除重复的元素,使每个元素只出现一次 并返回新的长度。
不要为另一个数组分配额外的空间,你必须用恒定的内存在原地做这件事。
例如,鉴于输入数组A = [1,1,2]。
你的函数应该返回length = 2,而A现在是[1,2]。
Python
注意:该代码每次删除一个重复值,原有数值的长度也会随之改变,所以我们应该动态考虑数组的长度。
def RDSA(arr):
arr_len = len(arr)
i = 0
if arr_len == 0:
return 0
while i < arr_len:
if i + 1 < arr_len:
if arr[i] == arr[i + 1]:
arr.pop(i) # pop之后,数组的长度也会随着改变。
arr_len = arr_len - 1 #
i = i - 1
i = i + 1
return arr_len
def RDSA(arr):
# 取消arr_len赋值。
i = 0
if len(arr)== 0:
return 0
while i < len(arr):
if i + 1 < len(arr):
if arr[i] == arr[i + 1]:
arr.pop(i)
i = i - 1
i = i + 1
return arr_len
# 使用 numpy
import numpy as np
def remove_arrary(arr):
b = np.unique(np_arr) # np.unique会对数组自动去重排序,不管arr是普通数组还是np数组,np.unique()都可以
return len(b) # 节省空间就直接len(np.unique(np_arr))
C 语言
注意:该方法只是将后面的数提前,用以覆盖原来的重复值,原有的数组长度和内存是没有改变的,如果,你用原来的数组长度进行输出,则会输出新数组意外的数值,
int remove_arrary(int* arr, int len)
{
int index = 0;
if (len == 0)
return 0;
for(int i=1; i<len; i++)
{
if (arr[index] !=arr[i])
{
index =index + 1;
arr[index] = arr[i];
}
}
return index + 1; //因为index从0开始的,所以长度的话要加 1.
}
int main(void) {
int a[5] = {0,1,1,2,2};
int new_len = remove_arrary(a,5);
printf("new_len = %d \n", new_len);
for(int i=0;i<new_len;i++)
printf("%d ",a[i]);
return 0;
}
java 代码
该算法与上述C语言实现的一致。
package leetcode;
public class Solution {
public int removeDuplicates(int[] nums) {
if (nums.length == 0)
return 0;
int index = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] != nums[index]) {
index++;
nums[index] = nums[i];
}
}
index += 1;
return index;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Solution solution = new Solution();
int[] num = { 0, 1, 1, 2, 3 };
//int num2[] = new int[] { 0, 1, 1, 2, 3 }; //为了让熟悉C语言的熟悉,int num[]也可以
//int[] num3 = new int[] { 0, 1, 1, 2, 3 }; //同样正确
// 动态初始化
// int num4[] = new int[4];
// num4[0] = 4;
int new_len = solution.removeDuplicates(num);
for (int i = 0; i < new_len; i++) {
System.out.print(" " + num[i]); // println换行 print不换行
}
}
}
如果最多允许重复两次呢?例如,给定的排序数组A = [1,1,1,2,2,3]。
你的函数应该返回 length = 5,而A现在是[1,1,2,2,3]。
C 语言
int remove_arrary2(int* arr, int len)
{
int index = 2;
if (len <= 2)
return len;
for(int i=2; i<len; i++)
{
if (arr[index-2] !=arr[i])
{
arr[index] = arr[i];
index =index + 1; //注意和上述的不同,这里先复制再加1等价于i++操作
}
}
return index;
}
int main(void) {
hello();
int a[6] = {1,1,1,2,2,3};
int new_len = remove_arrary2(a,6);
printf("new_len = %d \n", new_len);
for(int i=0;i<new_len;i++)
printf("%d ",a[i]);
return 0;
}
2. 在旋转排序的数组中搜索【Search in Rotated Sorted Array】
假设一个排序的数组在你事先不知道的某个支点上被旋转了。
(例如,0 1 2 4 5 6 7可能变成4 5 6 7 0 1 2)。
你被赋予一个目标值来搜索。如果在数组中找到,返回其索引,否则返回-1。
你可以假设数组中没有重复的东西存在。
Python
def BLA(arr, key):
'''
二分查找用于已经排好序的数组
'''
low = 0
high = len(arr)
while low <= high:
mid = low + (high - low) / 2
if key == arr[mid]:
return mid
if key < arr[mid]:
high = mid - 1
if key > arr[mid]:
low = mid + 1
return -1
将上述的二分查找变形
def BLA(arr, key):
low = 0
high = len(arr)
while low <= high:
mid = low + (high - low) / 2
if key == arr[mid]:
return mid
if arr[low] < arr[mid]: # 拐点不在左边
if arr[low] <= key & arr[mid] > key: # 拐点不在左边, 左边有序, key 值在左边
high = mid
else:
low = mid + 1 # 拐点不在左边, key 值在右边
elif arr[low] > arr[mid]: # 拐点在左边出现, 左边无序
if arr[mid] < key & key < arr[high-1]: # 拐点在左边出现, key值在右边
low = mid + 1
else:
high = mid # 拐点在左边出现, key之也在左边
else:
++low # 跳过重复的一项
return -1
3. 返回两个排序数组的中位数/第K大的数【Median of Two Sorted Arrays】
解析:这是一道非常经典的题。这题更通用的形式是,给定两个已经排序好的数组,找到两者所有元素中第k大的元素。
O(m + n)的解法比较直观,直接merge两个数组,然后求第k大的元素。
- 不过我们仅仅需要第k大的元素,是不需要“排序”这么昂贵的操作的。可以用一个计数器记录当前已经找到第m大的元素了。
- 同时我们使用两个指针pA和pB, 分别指向A和B数组的第一个元素,使用类似于merge sort的原理。
- 如果数组A当前元素小,那么pA++, 同时m++;如果数组B当前元素小,那么pB++, 同时m++。
- 最终当m等于k的时候,就得到了我们的答案,O(k) 时间,0(1)空间。
但是,当k很接近m+n的时候,这个方法还是O(m+n)的。有没有更好的方案呢?
- 我们可以考虑从k入手。如果我们每次都能够删除一个一定在第 k大元素之前的元素,那么我们需要进行k次。
但是如果每次我们都删除一半呢?
- 由于A和B都是有序的,我们应该充分利用这里面的信息,类似于二分查找,也是充分利用了“有序”。
- 假设A和B的元素个数都大于k/2,我们将A的第k/2个元素(即A[k/2-1]) 和B的第k/2个元素(即B[k/2-1])进行比较,
有以下三种情况(为了简化这里先假设k为偶数,所得到的结论对于k是奇数也是成立的):
-
A[k/2-1] = B[k/2-1]
-
A[k/2-1] > B[k/2-1]
-
A[k/2-1] < B[k/2-1]
-
如果A[k/2-1] < B[k/2-1], 意味着A[0] 到A[k/2-1] 的肯定在A U B的top k元素的范围内,换句话说,A[k/2-1] 不可能大于A U B的第k大元素。留给读者证明。因此,我们可以放心的删除A数组的这k/2个元素。
-
同理,当A[k/2-1] > B[/2-1] 时,可以删除B数组的k/2个元素。
-
当A[k/2-1] = B[k/2-1]时,说明找到了第k大的元素,直接返回A[k/2-1] 或B[k/2-1]即可。
因此,我们可以写一个递归函数。那么函数什么时候应该终止呢?
- 当A或B是空时,直接返回B[k-1]或A[k-1];
- 当k=1是,返回min(A[0], B[0]);
- 当A[k/2-1] == B[k/2-1]时,返回A[k/2-1] 或B[k/2-1]
Python
def find_median_sorted_arrays(arr1, arr2):
"""
给定两个大小分别为m和n的正序(从小到大)数组arr1和arr2。请你找出并返回这两个正序数组的中位数
:param arr1:排好序的数组 1
:param arr2:排好序的数组 2
:return: 两个数的中位数
"""
p, q = 0, 0
ans = [] # 创建一个新数组,
while p < len(arr1) and q < len(arr2):
if arr1[p] < arr1[q]:
ans.append(arr1[p]) # arr1 < arr2,将arr1添加到ans数组中
p += 1 # arr1 指针加1
else:
ans.append(arr2[q]) # 将arr2添加到ans数组中
q += 1 # arr2 指针加1
# arr1和arr2中有一个数组已经添加完。
ans += arr1[p:]
ans += arr2[q:]
"""
上述代码可用以下代替
ans = arr1 + arr2
ans.sort() # 列表.sort(reverse=True)降序排序
"""
L = len(ans)
if L % 2 == 1: # 奇数
return float(ans[L // 2]) # //是表示向下取整的除法
else:
return float((ans[L // 2] + ans[L // 2 - 1]) / 2) # 偶数,中位数为中间两个数的和/2
def top_k(num1, num2, k):
ans = num1 + num2
ans.sort()
return ans[-k]
java 代码
public float find_median_sorted_arrays(int[] arr1, int[] arr2) {
int p = 0;
int q = 0;
float mid_num = 0;
ArrayList<Integer> list = new ArrayList<Integer>();
while (p < arr1.length & q < arr2.length) {
if (arr1[p] < arr2[q]) {
list.add(arr1[p]);
p += 1;
} else {
list.add(arr2[q]);
q += 1;
}
}
if (q < arr2.length) { // q没完
while (q < arr2.length) {
list.add(arr2[q]);
q += 1;
}
} else {
while (p < arr1.length) {
list.add(arr1[p]);
p += 1;
}
}
int len = list.size();
int mid = len / 2; // 向上取整函数 Math.ceil(param);向下取整函数 Math.floor(param)
System.out.println(list);
if (len % 2 == 1) {
mid_num = list.get(mid);
} else {
mid_num = (float) (list.get(mid) + list.get(mid - 1)) / 2; // 两个数相除需要强转一下,不然就是取整。
}
return mid_num;
}
public int top_k(int[] arr1, int[] arr2, int k) {
ArrayList<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < (arr1.length); i++) {
list.add(arr1[i]);
}
for (int i = 0; i < arr2.length; i++) {
list.add(arr2[i]);
}
// Comparator.naturalOrder()升序(自然排序)Comparator.reverseOrder()降序
list.sort(Comparator.naturalOrder());
return list.get(list.size()-k);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Solution solution = new Solution();
int[] num = { 0, 2, 4, 6, 8 };
int num2[] = new int[] { 1, 3, 5, 7, 9 };
float min = solution.find_median_sorted_arrays(num, num2);
System.out.println(min);
int top_K = solution.top_k(num, num2, 3);
System.out.println(top_K);
}
}
4. 找出最长的连续元素序列的长度【Longest Consecutive Sequence】
给出一个未排序的整数阵列,找出最长的连续元素序列的长度。
例如,给定[100, 4, 200, 1, 3, 2],最长的连续元素序列是[1,2, 3, 4]. 返回其长度:4。
你的算法应该以O(n)的复杂度运行。
Python
解析:
先去重,然后遍历数组,有比当前数字小的存在于数组中直接跳过,找寻序列中最小的那个,然后向上查找,存储查找到的最长序列长度并进行存储。
def longest_consecutive_sequence(nums): # set() 函数创建一个无序不重复元素集,可进行关系测试,删除重复数据
res = 0 # 最长序列长度
nums = set(nums) # 去重
for num in nums: # 遍历数组
if num - 1 not in nums: # 从序列中最小的那一个开始查询
current_num = num
now_long = 1 # 当前最长长度
while current_num + 1 in nums: # 查询当前值的后面一个值是否在数组中
current_num += 1
now_long += 1
res = max(res, now_long) # 比较返回最大的那个
return res # 返回最大值
if __name__ == '__main__':
arr = [100, 4, 200, 1, 3, 2]
print(longest_consecutive_sequence(arr))
使用 numpy 的 unique 方法
def longest_consecutive_sequence2(nums):
temp = np.unique(nums)
res = 0
index = 1
for i in range(1, len(temp)):
if temp[i] == temp[i - 1] + 1:
index = index + 1
if res < index:
res = index
else:
index = 0
return res
if __name__ == '__main__':
arr = [100, 4, 200, 1, 3, 2, 5]
print(longest_consecutive_sequence2(arr))
java代码
解析:
- 将所有数放进哈希表,只枚举每一段的最小值(比如枚举x的时候,保证x存在且x - 1不存在),然后依次枚举后面的数是否存在。(由于不清楚序列的长度有多少,应该用while来枚举后面的序列)
- 避免重复,保证每个数只枚举一次,枚举完就删掉。
public int longest_consecutive(int[] nums) {
Set<Integer> set = new HashSet<>();
for (int num : nums)
set.add(num); //添加数组元素进入set
int res = 0;
for (int x : nums) { //迭代遍历数组中的元素
int y = x;
if (set.contains(x) && !set.contains(x - 1)) {
set.remove(x); //移除
// 寻找序列长度,
while (set.contains(y + 1)) {
y++;
set.remove(y); //先加1,然后再把存在的y + 1从set中删除
}
res = Math.max(res, y - x + 1); //比较两个值,返回最大值
}
}
return res;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int nums[] = {100,4,200,1,3,2,5};
Solution solution = new Solution();
int new_len = solution.longest_consecutive(nums);
System.out.println(new_len);
}
5. 两个数相加等于特定值【Two Sum】
给定一个整数数组,找出两个数字,使它们相加等于一个特定的目标数字。函数twoSum应该返回两个数字的索引,使它们加起来等于目标数,其中index1必须小于index2。请注意,你返回的答案(包括index1和index2)不是基于零
字典的使用
xiaoming = {"name": "小明",
"age": 18,
"gender": True,
"height": 1.75}
# for 循环内部使用的 `key 的变量` in 字典
for k in xiaoming:
print("%s: %s" % (k, xiaoming[k]))
Python
def two_sum(nums, target): # 定义方法twosum,
dic = dict() # 利用python中的字典记录每个元素出现的位置,也就是其他语言的哈希表。
'''
# 循环遍历字典中的对象,获得索引和值。
# enumerate参数为可遍历/可迭代的对象(如列表、字符串)
# enumerate多用于在for循环中得到计数,利用它可以同时获得索引和值
'''
for index, value in enumerate(nums):
sub = target - value
if sub in dic: # 字典中有与 value 匹配的数值,sub为key值,dic[sub]是value
return [dic[sub], index] # 返回两个数的索引
else:
dic[value] = index # 字典中没有与 value 匹配的数值,将value以key,index的形式存入字典中。
if __name__ == '__main__':
arr = [2, 7, 11, 15]
print(two_sum(arr, 26))
java代码
复杂度分析:
- 时间复杂度:O(n)
- 空间复杂度:O(n)
解题思路:所求的两个值,一旦有一个已经在哈希表中,那么另一个值便可在数组遍历过程中找出
public int[] twoSum(int[] nums, int target) {
/*
* 这里我们把数组中的值作为map里的key值,数组中的index作为value存储在了map中
*/
HashMap<Integer, Integer> m = new HashMap<Integer, Integer>();
int[] res = new int[2]; // 用来存储两个数字的索引
for (int i = 0; i < nums.length; ++i) {
if (m.containsKey(target - nums[i])) { // 检查 hashMap 中是否存在指定的 key键 对应的映射关系。
res[0] = i;
res[1] = m.get(target - nums[i]); // 获取指定 key 对应对 value
break;
}
m.put(nums[i], i); // 将键/值对添加到 hashMap 中
}
return res;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int arr[] = { 2, 7, 11, 15 };
int tar = 9;
Solution solution = new Solution();
int[] two_index = solution.twoSum(arr, tar);
System.out.println(two_index[0] + " " + two_index[1]);
}
6. 排列组合 Permutation Sequence
要求:
集合
[
1
,
2
,
3
,
.
.
.
.
,
n
]
[1,2,3,.... ,n]
[1,2,3,....,n] 共包含了
n
n
n 个独特的排列组合。通过按顺序列出并标明所有的排列组合,我们得到以下序列(即对于
n
=
3
n=3
n=3 而言)
- 给定 n n n 和 k k k,返回第 k k k 个互换序列。
- 注意:给定的 n n n 将在 1 1 1 到 9 9 9 之间(包括9)。
解析:
这道题是让求出
n
n
n 个数字的第
k
k
k 个排列组合,由于其特殊性,我们不用将所有的排列组合的情况都求出来,然后返回其第
k
k
k 个。
我们可以只求出第 k k k 个排列组合即可,那么难点就在于如何知道数字的排列顺序。首先我们要知道当 n = 3 n = 3 n=3 时,其排列组合共有 3 ! = 6 3! = 6 3!=6 种,当 n = 4 n = 4 n=4 时,其排列组合共有 4 ! = 24 4! = 24 4!=24 种,我们就以 n = 4 , k = 17 n = 4, k = 17 n=4,k=17 的情况来分析,所有排列组合情况如下
- 我们可以发现,每一位上 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4 分别都出现了 6 6 6 次,
- 当第一位上的数字确定了,后面三位上每个数字都出现了 2 2 2 次,
- 当第二位也确定了,后面的数字都只出现了 1 1 1 次,
- 当第三位确定了,那么第四位上的数字也只能出现 1 1 1 次,
- 那么下面我们来看 k = 17 k = 17 k=17 这种情况的每位数字如何确定,
由于 k = 17 k = 17 k=17 是转化为数组下标为 16 16 16:
- 最高位可取 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4 中的一个,每个数字出现 3 ! = 6 3!= 6 3!=6 次,所以 k = 16 k = 16 k=16 的第一位数字的下标为 16 / 6 = 2 16 / 6 = 2 16/6=2,即 3 3 3 被取出
- 第二位此时从1,2,4 中取一个, k ′ = 16 % ( 3 ! ) = 4 k' = 16 \% (3!) = 4 k′=16%(3!)=4,而剩下的每个数字出现 2 ! = 2 2!= 2 2!=2 次,所以第二数字的下标为 4 / 2 = 2 4 / 2 = 2 4/2=2,即 4 4 4 被取出
- 第三位此时从 1 , 2 1,2 1,2 中去一个, k ′ ′ = 4 % ( 2 ! ) = 0 k'' = 4 \% (2!) = 0 k′′=4%(2!)=0,而剩下的每个数字出现 1 ! = 1 1!= 1 1!=1次,所以第三个数字的下标为 0 / 1 = 0 0 / 1 = 0 0/1=0,即 1 1 1 被取出
- 第四位是从 2 2 2 中取一个, k ′ ′ ′ = 0 % ( 1 ! ) = 0 k''' = 0 \% (1!) = 0 k′′′=0%(1!)=0,而剩下的每个数字出现 0 ! = 1 0!= 1 0!=1次,所以第四个数字的下标为 0 / 0 = 0 0 / 0= 0 0/0=0,即 2 2 2 被取出
那么我们就可以找出规律了
k(1) = k
a(1) = k(1)/ (n - 1)!
k(2) = k(1) % (n - 1)!
a2 = k(2) / (n - 2)!
k(3) = k(2) % (n - 2)!
a(3) = k(3) / (n - 3)!
k(4) = k(3) % (n - 3)!
a(4) = k(4) / (n - 4)!
...
k(n-1) = k(n-2) / 2!
a(n-1) = k(n-1) / 1!
k(n) = k(n-1) % 1!
a(n) = k(n) / 0!
Python
def get_permutation(n, k):
# 首先进行累乘,算出当 n位数值有多少种组合
fact = [0] * (n + 1) # n=4 [0, 0, 0, 0, 0]
fact[0] = 1 # 不从零个数开始 底下的for直接从1开始,然后到n
for i in range(1, n + 1):
fact[i] = fact[i - 1] * i # 0~n的阶乘[1, 1, 2, 6, 24] 分别为0!, 1!, 2!, 3!, 4!
temp = [0] * n # n=4 [0, 0, 0, 0]
for i in range(1, n + 1):
temp[i - 1] = i # [1, 2, 3, 4],用来选取各个位置上的值, 选完之后,temp 需要把该数移除。
result = [] # 存储结果
k = k - 1
# 下面是核心思想
for i in range(1, n):
index = k // fact[n - i] # // 向下取整
result.append(temp[index]) # 在result末尾追加temp[index]值
temp.remove(temp[index])
k -= (index * fact[n - i]) # 取余数, 更新k值,
result.append(temp[0])
return result
if __name__ == '__main__':
print(get_permutation(4, 17))
java 代码
public String getPermutation(int n, int k) {
k = k - 1; // 下标从0开始 ***
StringBuffer st = new StringBuffer(); // 使用StringBuffer()原因是 string没有append方法。
ArrayList<Integer> num = new ArrayList<Integer>();
if (n == 0)
return st.toString();
int fact = 1;
for (int i = 1; i < n; i++) { // (n-1)!
fact = fact * i;
}
for (int i = 1; i <= n; i++) { //假设n=4 num = [1,2,3,4]
num.add(i);
}
int kx = k;
int count = n;
while (count > 0) {
int index = kx / fact; //计算该位置的应该取num中哪个值,index为值的索引
st.append(num.get(index)); // 将值追加到st末尾
num.remove(index); // 将该值从num中删除
kx = kx % fact; // 更新k值
if ((count - 1) != 0)
fact = fact / (count - 1); // 更新阶乘,假如之前是(n-1)!,现在(n-2)!
count--;
}
return st.toString();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Solution solution = new Solution();
String string = solution.getPermutation(4, 17);
System.out.println(string);
}