Leetcode数组考察(Python、C语言和java实现)

数组

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代码

解析:

  1. 将所有数放进哈希表,只枚举每一段的最小值(比如枚举x的时候,保证x存在且x - 1不存在),然后依次枚举后面的数是否存在。(由于不清楚序列的长度有多少,应该用while来枚举后面的序列)
  2. 避免重复,保证每个数只枚举一次,枚举完就删掉。
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] [123....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 1234 分别都出现了 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 1234 中的一个,每个数字出现 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 12 中去一个, 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);
	}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值