数组中的问题

数组中的问题

关于数组

1、数组应用场景

排序:选择排序、插入排序、归并排序、快速排序

查找:二分查找

数据结构:栈、队列、堆

2、如何写出正确的程序

明确变量和含义;循环不变量;小数据量调试

举例:二分查找法:找中间,再往左或右序列中寻找。

对于有序数列,才能使用二分查找法

public static int binarySearch(Comparable[] arr, int n, Comparable target){

        int l = 0, r = n - 1; // 在[l...r]的范围里寻找target
        while(l <= r){    // 当 l == r时,区间[l...r]依然是有效的
            int mid = l + (r - l) / 2;
            if(arr[mid].compareTo(target) == 0) return mid;
            if(target.compareTo(arr[mid]) > 0)
                l = mid + 1;  // target在[mid+1...r]中; [l...mid]一定没有target
            else    // target < arr[mid]
                r = mid - 1;  // target在[l...mid-1]中; [mid...r]一定没有target
        }

        return -1;
    }

循环中的循环不变性

改变变量定义,依然可以写出正确的代码:

//上述代码中,若对边界的定义改变:
int l=0,r=n;//意味着在[l……r)的范围里寻找target
while(l<r){//当l==r时,区间[l……r)是无效的
    int mid=l+(r-l)/2;//避免整型溢出问题
    if(arr[mid].compareTo(target)==0)return mid;
    if(target.compareTo(arr[mid]) > 0)
        l=mid+1;//target在[mid+1...r)中寻找
    else
        r=mid;//target在[l...mid)中
}

Move Zeros

leetcode283:将数组中所有的0挪到数组的末尾,而维持其他所有非0元素的相对位置。

思路:扫描数组,将所有非0元素选出,填补到数组前面,其他元素变为0.

方法一:开辟一个新的数组存放非0元素,空间复杂度为O(n)

	public void moveZeroes(int[] nums) {
		ArrayList<Integer> nonZeroElements=new ArrayList<>();
		for(int i=0;i<nums.length;i++) {
			if(nums[i]!=0)
				nonZeroElements.add(nums[i]);
		}
		for(int i=0;i<nonZeroElements.size();i++)
			nums[i]=nonZeroElements.get(i);
		for(int i=nonZeroElements.size();i<nums.length;i++)
			nums[i]=0;
		
	}

方法二:设置新的index:k,指示非0元素按顺序排列在[0…k)中,空间复杂度为O(1)

方法三:将k和i位置的元素进行交换,i指示非0元素

	public void moveZeroes(int[] nums) {
		int k=0;
		for(int i=0;i<nums.length;i++)
			if(nums[i]!=0)
				swap(nums,k++,i);
	}

练习:27,26,80

基础算法思路的应用

75、Sort Colors:数组中元素取值只有012三种可能,进行原地排序。

没有思路,尝试暴力解法。再想是否能做的更好。普通排序O(nlogn)

(1)计数排序:分别统计0,1,2的个数,然后放回数组。O(n)

错误处理:assert…

(2)三路快速排序

image-20201113093717288
	public void sortColors(int[] nums) {
		int zero=-1;// [0...zero] == 0
		int two=nums.length; // [two...n-1] == 2
		for(int i=0;i<two;) {
			if(nums[i]==1)
				i++;
			else if(nums[i]==2) {//else if!!
				swap(nums,i,--two);	
			}
			else {
				assert nums[i] == 0;
                swap(nums, ++zero, i++);//注意i++
			}
		}
    }
	private void swap(int[] nums, int i, int j){
        int t = nums[i];
        nums[i]= nums[j];
        nums[j] = t;
    }

练习:88,215

88:归并排序中的归并。

215:寻找K大元素。利用快排partition,将pivot放置在了其正确的位置上。

对撞指针

167:在有序数组中寻找两个元素,使其和为target,返回索引。

是否可以使用相同元素?返回索引从0还是1开始?多个解怎么办?没有解怎么办?

题目保证一定有解且有唯一解。

(1)暴力解法O(n2),没有充分利用原数组的性质–有序

(2)有序?二分搜索?O(nlogn)

image-20201113101410954
public int[] twoSum(int[] numbers,int target){
	if(numbers.length<2||!isSorted(numbers))...
    for(int i=0;i<numbers.length-1;i++){
        int j=binarySearch(numbers,i+1,numbers.length-1,target-numbers[i]);//why i+1;
        if(j!=-1){
            int[] res={i+1;j+1};
            return res
        }
    }
}
private int binarySearch(int[] nums,int l,int r,int target)

(3)O(n)对撞指针

image-20201113101704105

nums[i]+nums[j]==target找到了!

nums[i]+nums[j]<target i++

nums[i]+nums[j]<target j–

	public int[] twoSum(int[] numbers, int target) {
		int l=0;
		int r=numbers.length-1;
		while(l<r) {
			if(numbers[l]+numbers[r]==target) {
				int[] res= {l+1,r+1};//下标从1开始
				return res;
			}
			else if(numbers[l]+numbers[r]<target)
				l++;
			else
				r--;
		}
		 throw new IllegalStateException("The input has no solution");
    }

练习

125、回文串

空字符串是否看做回文串?字符的定义?大小写问题

344、倒序字符串

345、元音字母翻转

11、Container with most water

双索引技术:滑动窗口

209、Minimum Size Subarray Sum

给定整型数组和数字s,找到数组中最短的一个连续子数组,使得连续子数组的数字和sum>=s,返回长度值。

  • 什么叫子数组

  • 没有解怎么办?返回0

(1)暴力解O(n3):遍历所有的连续数组

(2)双索引

image-20201114111515762

确定三点:

1)窗口内是什么?满足和>=s的长度最小的连续子数组

2)如何移动窗口的起始位置?窗口内值>s,则窗口向前移动

3)如何移动窗口的结束位置?窗口的结束位置就是遍历数组的指针

思路:

使用 left、right 两指针,分别表示窗口的左右边界。
移动右指针,扩大窗口,直到子数组达到目标值 target。
移动左指针,缩小窗口,直到子数组不满足目标值。

	// 时间复杂度: O(n)
	// 空间复杂度: O(1)
	public int minSubArrayLen(int s,int[] nums) {
		if(s <= 0 || nums == null)
            throw new IllegalArgumentException("Illigal Arguments");
		int l=0,r=-1;//nums[l...r]为我们的滑动窗口
		int sum=0;
		int res=nums.length+1;
		while(l<nums.length) {
			if(r+1<nums.length&&sum<s) {//r到达最右侧不能再拓展了
				sum+=nums[++r];//右边界向前拓展
			}
			else {
				sum-=nums[l++];
			}
			if(sum>=s)
				res=Math.min(res,r-l+1);
				
		}
		if(res==nums.length+1)
			return 0;
		return res;
	}

T3、Longest Substring without repeating characters

在一个字符串中寻找没有重复字母的最长子串。

滑动窗口:

a) 两个指针,代表左右边界

b) 每一步迭代,左指针+1;不断移动右指针

c) 左右指针都是只往前进的

问题:

字符集?只有字母?数字+字母?ASCII?

大小写是否敏感?

image-20201114165345924

如何记录重复字符?freq[256]或者使用Set

	public int lengthOfLongestSubstring(String s) {
		int[] freq=new int[256];
		int l=0,r=-1;
		int res=0;
		while(l<s.length()) {
			if(r+1<s.length()&&freq[s.charAt(r+1)]==0) {
				freq[s.charAt(++r)]++;
			}
			else {
				freq[s.charAt(l)]--;
				l++;
			}
			res=Math.max(res, r-l+1);
		}
		return res;
	}

练习(滑动窗口):

438:find all anagrams in a string

字符集?英文小写字母

返回解的顺序?任意

76(hard):

字符集?

没有解?

多个解?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值