leetcode_数组相关问题

283.零移动

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

说明:

  1. 必须在原数组上操作,不能拷贝额外的数组。
  2. 尽量减少操作次数。

 

思路一:保证非零元素的相对顺序,冒泡排序法,时间复杂度为O(n^2),空间复杂度为O(1)

class Solution {
    //保证非零元素的相对顺序,冒泡排序法,时间复杂度为O(n^2),空间复杂度为O(1)
    public void moveZeroes(int[] nums) {
        
        //优化的冒泡排序法,记录最后一次交换元素的位置
        int lastSwap = 0;
        for(int i=nums.length-1; i>0; i=lastSwap){
            lastSwap = 0;
            for(int j=0; j

思路二:双指针法 

class Solution {
    //双指针k,l   移动l,当l处的元素非零时,便与k处的元素交换位置,同时k加一,维护[0,k)为非零元素
    //时间复杂度为O(n),空间复杂度为O(1)
    public void moveZeroes(int[] nums) {
        int k=0, l=0;
        for(;l

 

 

腾讯2017暑期实习生编程题2:

把一个字符串的大写字母放到字符串的后面,各个字符的相对位置不变,且不能申请额外的空间。 

输入描述:  输入数据有多组,每组包含一个字符串s,且保证:1<=s.length<=1000.

输出描述:  对于每组数据,输出移位后的字符串。

输入例子1:  AkleBiCeilD

输出例子1:  kleieilABCD

 

思路一:冒泡排序

import java.util.*;

public class Main{
    
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        while(in.hasNext()){
            String s = in.nextLine();
            System.out.println(getResult(s));            
        }
    }
    
    public static String getResult(String input){
        char[] str = input.toCharArray();
        int lastSwap = 0;
        for(int i=str.length-1; i>0; i=lastSwap){
            lastSwap = 0;
            for(int j=0;j='A'&& c<='Z') && (d>='a'&& d<='z')){
                    char temp = str[j];
                    str[j] = str[j+1];
                    str[j+1] = temp;
                }
                
                lastSwap = j;
            }
        }
        
        StringBuilder sb = new StringBuilder();
        for(int i=0; i

思路二:双指针法,用两个指针,一个表示当前处理元素,一个表示下一个待放置小写字母的位置,每次处理将中间的所有大写字母移动一遍以保持大写字母原有顺序

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        while(in.hasNextLine()){
            String str = in.nextLine();
            System.out.println(getResult(str));
        }
    }
    
    public static String getResult(String s){
        char[] stochars = s.toCharArray();
        int k=0, l=0;
        for(;l'Z'){
                for(int p = k; p

 

26.删除排序数组中的排序项

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定数组 nums = [1,1,2], 

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 

你不需要考虑数组中超出新长度后面的元素。
 

示例 2:

给定 nums = [0,0,1,1,1,2,2,3,3,4],

函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。

你不需要考虑数组中超出新长度后面的元素。

 

思路:双指针法

class Solution {
    //[0,k]内为不重复的元素,l用于遍历
    public int removeDuplicates(int[] nums) {
        int k=0, l=0;
        for(; l

 

27. 移除元素

给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1:

给定 nums = [3,2,2,3], val = 3,

函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。

你不需要考虑数组中超出新长度后面的元素。

示例 2:

给定 nums = [0,1,2,2,3,0,4,2], val = 2,

函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。

注意这五个元素可为任意顺序。

你不需要考虑数组中超出新长度后面的元素。

思路:双指针法

class Solution {
    //[0,k)为非val的元素,l用于遍历数组
    public int removeElement(int[] nums, int val) {
        int k=0, l=0;
        for(; l

 

80. 删除排序数组中的重复项 II

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定 nums = [1,1,1,2,2,3],

函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。

你不需要考虑数组中超出新长度后面的元素。

示例 2:

给定 nums = [0,0,1,1,1,1,2,3,3],

函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。

你不需要考虑数组中超出新长度后面的元素。

思路:双指针法

class Solution {
    public int removeDuplicates(int[] nums) {
        //双指针,[0,k]为符合要求的数据
        int k=1,l=2;
        for(;l

 

75. 颜色分类(三路快排思想)

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

注意:
不能使用代码库中的排序函数来解决这道题。

示例:

输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]

思路一:计数排序

class Solution {
    public void sortColors(int[] nums) {
        int[] count = new int[3];
        for(int i=0; i

思路二:快速排序中partition过程中的三路快排思想 ,空间复杂度为O(1).

class Solution {
    public void sortColors(int[] nums) {
        //快速排序 partition过程中的三路快排思想
        //定义三个指针a,b,c
        //保证[0,a]的值为0,[a+1,b)的值为1,(b,c]的值为2
        int a=-1, b=0, c=nums.length;
        while(b

 

88. 合并两个有序数组 (归并排序中的归并 )

给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。

说明:

初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:

输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6],       n = 3

输出: [1,2,2,3,5,6]

思路:归并排序中归并的过程 

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        //归并排序中归并的过程
        int a=0, b=0;
        int[] arr = new int[m+n];
        
        for(int i=0; i=m){
                arr[i]=nums2[b++];
            }else{
                arr[i]=nums1[a++];
            }            
        }
        
        for(int i=0; i

 

215. 数组中的第K个最大元素

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5

示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:

你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

经典的topK问题:

思路一:进行排序后再选择(时间复杂度为O(n^2) 或 O(logn))

class Solution {
    public int findKthLargest(int[] nums, int k) {
        quickSort(nums, 0, nums.length-1);
        return nums[k-1];
    }
    
    private int positition(int[] arr, int l, int r){
        //随机选取元素进行positition操作
        int index = (int)(Math.random()*(r-l+1)+l);
        int temp = arr[index];
        arr[index] = arr[l];
        arr[l] = temp;
        int v = arr[l];
        
        //双路快排
        int i=l+1, j=r;
        while(true){
            while(i<=r && arr[i]>v){
                i++;
            }
            
            while(j>=l+1 && arr[j]j){
                break;
            }
            
            int temp0 = arr[i];
            arr[i] = arr[j];
            arr[j] = temp0;
            
            i++;
            j--;
        }
        
        int temp1 = arr[l];
        arr[l] = arr[j];
        arr[j] = temp1;
        
        return j;
    }
    
    private void quickSort(int[] arr, int l, int r){
        if(l>=r){
            return;
        }
        
        int p = positition(arr, l, r);
        quickSort(arr, l, p-1);
        quickSort(arr, p+1, r);
    }
}

思路二:使用优先队列(堆)

思路三:快速排序中partition过程的合理使用(使得时间复杂度为O(n) )

class Solution {
    public int findKthLargest(int[] nums, int k) {
       
        return findKth(nums, 0, nums.length-1, k-1);            
    }
    
    private int positition(int[] arr, int l, int r){
        //随机选取标定点
        int index = (int)(Math.random()*(r-l+1)+l);
        int temp = arr[index];
        arr[index] = arr[l];
        arr[l] = temp;
        int v = arr[l];
        
        //双路快排
        int i=l+1, j=r;
        while (true){                      //while判断为真, 可以保证即使只有两个元素时,即i=l+1=j 时,也可以进入循环
 
            while (i<=r && arr[i]>v){     //i停下的位置一定是小于或等于v的位置
                i++;
            }
            while (j>=l+1 && arr[j]j){      //只能取大于
                break;
            }
            
            int temp0 = arr[i];
            arr[i]=arr[j];
            arr[j] = temp0;
            
            i++;
            j--;
        }
        int temp1 = arr[l];
        arr[l] = arr[j];
        arr[j]= temp1;     //j落在最后一个大于等于v的位置, i落在第一个小于等于v的位置,最终i不等于j, 而l处需要一个大于v的值,所以只能和 j 处的交换
        return j;   
    }
    
    private int findKth(int[] arr, int l, int r, int k){
        if(l==r){
            return arr[l];
        }
        
        int p = positition(arr, l, r);
        if(k==p){
            return arr[p];
        }else if(k

 

双索引中的对撞指针

167. 两数之和 II - 输入有序数组

给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。

函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

说明:

返回的下标值(index1 和 index2)不是从零开始的。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:

输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

思路一:暴力求解 ,时间复杂度为O(n^2)

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int[] res = new int[2];
        tag: for(int i=0; i

思路二:利用数组的有序性,使用二分查找法降低时间复杂度

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int[] res = new int[2];
        tag:for(int i=0; i o){
                r = mid-1;
            }else{
                l=mid+1;
            }          
        }
        return -1;
    }
}

思路三:充分利用数组的有序性,使用对撞指针,时间复杂度为O(n)

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int[] res = new int[2];
        
        //对撞指针
        int k=0, l=numbers.length-1;
        while(numbers[k]+numbers[l]!=target){            
            if(numbers[k]+numbers[l]>target){
                l--;
            }else{
                k++;
            }                        
        }
        res[0] = k+1;
        res[1] = l+1;
        
        return res;      
    }
}

125. 验证回文串

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。

说明:本题中,我们将空字符串定义为有效的回文串。

示例 1:

输入: "A man, a plan, a canal: Panama"
输出: true
示例 2:

输入: "race a car"
输出: false

思路:对撞指针

class Solution {
    public boolean isPalindrome(String s) {
        
        //预处理
        s = s.toLowerCase();
        StringBuilder sb = new StringBuilder();
        for(int i=0; i='0' && c<='9') || (c>='a' && c<='z')){
                sb.append(c);
            }
        }
        
        String ss = sb.toString();
        
        if(ss.length()<2){
            return true;
        }
        
        //对撞指针
        int i=0, j=ss.length()-1;
        while(i<=j){
            if(ss.charAt(i) != ss.charAt(j)){
                return false;
            }
            i++;
            j--;
        }
        
        return true;
    }
}

344. 反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。


示例 1:
输入:["h","e","l","l","o"]
输出:["o","l","l","e","h"]

示例 2:
输入:["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]

思路:对撞指针

class Solution {
    public void reverseString(char[] s) {
        int i=0, j=s.length-1;
        while(i<=j){
            
            char temp = s[i];
            s[i] = s[j];
            s[j] = temp;
                       
            i++;
            j--;
        }
    }
}

345. 反转字符串中的元音字母

编写一个函数,以字符串作为输入,反转该字符串中的元音字母。

示例 1:

输入: "hello"
输出: "holle"

示例 2:

输入: "leetcode"
输出: "leotcede"
说明:
元音字母不包含字母"y"。

思路:对撞指针

class Solution {
    public String reverseVowels(String s) {
        String o = "aeiouAEIOU";
        
        char[] cs = s.toCharArray();
        int k=0, l=s.length()-1;
        while(k0){
                n = s.charAt(--l);
            }
            
            if(kl时,二次交换发生
                char temp = cs[k];
                cs[k] = cs[l];
                cs[l] = temp;
            }
                
            k++;
            l--;
        }
        
        return new String(cs);
    }
}

 

11. 盛最多水的容器

给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器,且 n 的值至少为 2。

图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例:
输入: [1,8,6,2,5,4,8,3,7]
输出: 49

思路一:暴力求解

思路二:对撞指针法

class Solution {
    public int maxArea(int[] height) {        
        
        int l=0, r=height.length-1;
        
        int i=l, j=r;
        int maxV = (j-i)*Math.min(height[i],height[j]);
        
        while(imaxV){
                maxV = (j-i)*Math.min(height[i],height[j]);                
            }  
        }
                   
        return maxV;        
    }
}

 

双指针中的滑动窗口

209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。

示例: 

输入: s = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。

思路:双指针滑动窗口 

class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        
        int l=0, r=nums.length-1;
        int length = r+2;   //数组的最长长度为r+1
        
        int i=l, j=l-1;    //滑动串口为[i,j], j= s && j-i+1

3. 无重复字符的最长子串

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:
输入: "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

 思路:双指针滑动窗口

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int length = 0;
        TreeMap map = new TreeMap<>(); //记录某个字符最后出现的位置
        
        char[] cs = s.toCharArray();
        int i=0, j=-1;
        while(j+1=i){
                        i=map.get(cs[j])+1;                        
                    }
                    map.put(cs[j], j);
                } 
                if(j-i+1>length){
                    length = j-i+1;
                }         
        }
        
        return length;
    }
}

 

438. 找到字符串中所有字母异位词

给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。

字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。

说明:
字母异位词指字母相同,但排列不同的字符串。
不考虑答案输出的顺序。

示例 1:
输入:
s: "cbaebabacd" p: "abc"
输出:
[0, 6]

解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的字母异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的字母异位词。

 示例 2:
输入:
s: "abab" p: "ab"
输出:
[0, 1, 2]

解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的字母异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的字母异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的字母异位词。

思路:双指针滑动窗口

class Solution {
    public List findAnagrams(String s, String p) {
        ArrayList list = new ArrayList<>();
        
        TreeMap pmap = new TreeMap<>();  //记录p中的元素
        TreeMap smap = new TreeMap<>();  //记录滑动窗口中的元素
        
        //统计p中的词频, 记录p中的内容
        for(char c : p.toCharArray()){
            if(!pmap.containsKey(c)){
                pmap.put(c, 1);
            }else{
                pmap.put(c, pmap.get(c)+1);
            }
        }
        
        int start=0, end=0;  //滑动窗口的两个边界 [start, end]
        int count=0;         //当滑动窗口中包含了p中的所有元素时,count=p.length()
        for(; end

 

76. 最小覆盖子串

给定一个字符串 S 和一个字符串 T,请在 S 中找出包含 T 所有字母的最小子串。

示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"

说明:
如果 S 中不存这样的子串,则返回空字符串 ""。
如果 S 中存在这样的子串,我们保证它是唯一的答案。

思路: 双指针滑动窗口

class Solution {
    public String minWindow(String s, String t) {
        int length = s.length()+1;
        String res = "";
        
        char[] cs = s.toCharArray();
        char[] ct = t.toCharArray();
        TreeMap tmap = new TreeMap<>();  //记录t中的内容
        TreeMap smap = new TreeMap<>();  //记录滑动窗口中的内容
        
        for(char c : ct){
            if(!tmap.containsKey(c)){
                tmap.put(c, 1);
            }else{
                tmap.put(c, tmap.get(c)+1);
            }
        }
        
        int count=0;  //当count=t.length()时,表示窗口中包含了t中的所有元素
        int start = 0, end = 0;  //窗口的两个边界 [start, end]
        for(; end

 

你可能感兴趣的:(leetcode)