数组,链表,跳表(双指针法)Array例题

核心思路:空间换时间+升维

数组
连续的内存空间,支持随机访问,时间复杂度 O(1)
插入、删除操作比较低效O(n)

链表
单链表、双向链表、循环链表、双向循环链表
更适合插入、删除操作频繁的场景,时间复杂度 O(1)
访问时遍历链表 ,平均情况时间复杂度为O(n)

跳表
空间换时间,多级索引来提高查询的效率,实现了基于链表的“二分查找”,是一种动态数据结构,支持快速的插入、删除、查找操作,时间复杂度为O(nlogn)

283.移动零

解法一 双指针 (j始终记录下一个非零元素的位置)

public void moveZeroes(int[] nums){
	int j = 0;
	for(int i = 0 ;i < nums.length; ++i){
		if (nums[i] != 0){
			nums[j] = nums[i];
			if(i!=j){
				nums[i] = 0;
			}
			j++;
		}
	}
}
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

解法二 遇零交换 注意python交换的特殊用法

def moveZeroes(self , nums):
	zero = 0
	for i in xrange( len(nums) ):
		if nums[i] != 0:
			nums[i], nums[zero] = nums[zero], num[i]
			zero += 1

11.盛最多水的容器

解法一 暴力求解 枚举O(n^2)

public int maxArea(int[] a){
	int max = 0;
	for(int i = 0;i <a.length -1; ++i){
		for(int j = i+1; j<a.length; ++j){
			int area = (j-i)*Math.min(a[i],a[j]);
			max = Math.max(max,area);
		}
	}return max;
}
输入:[1,8,6,2,5,4,8,3,7]
输出:49

解法二 从两边往中间筛选更高的(左右夹逼法)O(N)

public int maxArea(int[] a){
	int max = 0;
	for(int i = 0, j = a.length- 1 ; i <j ;){
		int minHight = a[i] < a[j] ? a[i++] : a[j--];
		int area = (j - i + 1)* minHight;
		max = Math.max(max,area);
	}return max;
}

70.爬楼梯

1:1
2:2
3:f(1)+f(2)
n : f(n)=f(n-1)+f(n-2)
不缓存中间变量,只保留最后三个值

def climbStairs(self, n):
	    if(n <= 2):return n
	    f1 , f2 , f3 = 1 , 2 , 3
	    for i in range(3, n+1):
		    f3 = f1 + f2
		    f1 = f2
		    f2 = f3
	    return f3

1.两数之和

解法一 枚举不同的下标O(n^2)

public int[] twoSum(int[] nums, int target) {
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[j] == target - nums[i]) {
                    return new int[] { i, j };
                }
            }
        }
        throw new IllegalArgumentException("No two sum solution");
    }
    
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

解法二 使用两次迭代。O(n)

在第一次迭代中,将每个元素的值和它的索引添加到表中。在第二次迭代中,检查每个元素所对应的目标元素(target - nums[i])是否存在于表中。
注意,该目标元素不能是 nums[i] 本身

public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            map.put(nums[i], i);
        }
        for (int i = 0; i < nums.length; i++) {
            int complement = target - nums[i];
            if (map.containsKey(complement) && map.get(complement) != i) {
                return new int[] { i, map.get(complement) };
            }
        }
        throw new IllegalArgumentException("No two sum solution");
    }

解法三 一遍哈希表 O(n)
在进行迭代并将元素插入到表中的同时,我们回过头来检查表中是否已经存在当前元素所对应的目标元素。如果它存在,那我们已经找到了对应解,并立即将其返回。

public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int complement = target - nums[i];
            if (map.containsKey(complement)) {
                return new int[] { map.get(complement), i };
            }
            map.put(nums[i], i);
        }
        throw new IllegalArgumentException("No two sum solution");
    }

15.三数之和

给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]

解法一 暴力求解 三种循环 O(n^3)
解法二 hash表来记录 a, b , a+b = -c

解法三 左右下标推进;双指针动态消去无效解来优化效率
O(N^2):其中固定指针k循环复杂度 O(N),双指针 i,j 复杂度 O(N)。

python:

def threeSum(self, nums: [int]) -> [[int]]:
        nums.sort()
        res, k = [], 0
        for k in range(len(nums) - 2):
            if nums[k] > 0: break # 1. because of j > i > k.
            if k > 0 and nums[k] == nums[k - 1]: continue # 2. skip the same `nums[k]`.
            i, j = k + 1, len(nums) - 1
            while i < j: # 3. double pointer
                s = nums[k] + nums[i] + nums[j]
                if s < 0:
                    i += 1
                    while i < j and nums[i] == nums[i - 1]: i += 1
                elif s > 0:
                    j -= 1
                    while i < j and nums[j] == nums[j + 1]: j -= 1
                else:
                    res.append([nums[k], nums[i], nums[j]])
                    i += 1
                    j -= 1
                    while i < j and nums[i] == nums[i - 1]: i += 1
                    while i < j and nums[j] == nums[j + 1]: j -= 1
        return res

java:

 public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        for(int k = 0; k < nums.length - 2; k++){
            if(nums[k] > 0) break;
            if(k > 0 && nums[k] == nums[k - 1]) continue;
            int i = k + 1, j = nums.length - 1;
            while(i < j){
                int sum = nums[k] + nums[i] + nums[j];
                if(sum < 0){
                    while(i < j && nums[i] == nums[++i]);
                } else if (sum > 0) {
                    while(i < j && nums[j] == nums[--j]);
                } else {
                    res.add(new ArrayList<Integer>(Arrays.asList(nums[k], nums[i], nums[j])));
                    while(i < j && nums[i] == nums[++i]);
                    while(i < j && nums[j] == nums[--j]);
                }
            }
        }
        return res;
    }

1.排序,从小到大
2.固定最左数字的指针 k,双指针 i,j 分设在数组索引 (k, len(nums)) 两端
当i < j时循环计算s = nums[k] + nums[i] + nums[j]
当s < 0时,i += 1并跳过所有重复的nums[i];
当s > 0时,j -= 1并跳过所有重复的nums[j];
注:
(1)当 nums[k] > 0 时直接break跳出
(2)当 k > 0且nums[k] == nums[k - 1]时即跳过

环形链表

解法一:哈希表O(N)
检查一个结点此前是否被访问过来判断链表是否为环形链表

public boolean hasCycle(ListNode head) {
    Set<ListNode> nodesSeen = new HashSet<>();
    while (head != null) {
        if (nodesSeen.contains(head)) {
            return true;
        } else {
            nodesSeen.add(head);
        }
        head = head.next;
    }
    return false;
}

解法二:快慢指针O(n)
java

public boolean hasCycle(ListNode head) {
        ListNode fast = head, slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) return true;
        }
        return false;
    }

python

def hasCycle(self, head):
        fast, slow = head, head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
            if fast == slow: return True
        return False

空间复杂度:O(1),只使用了慢指针和快指针两个结点

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值