给定一个数组
nums
,编写一个函数将所有0
移动到数组的末尾,同时保持非零元素的相对顺序。示例:
输入:[0,1,0,3,12]
输出:[1,3,12,0,0]
说明:
- 必须在原数组上操作,不能拷贝额外的数组。
- 尽量减少操作次数。
思路一:保证非零元素的相对顺序,冒泡排序法,时间复杂度为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
把一个字符串的大写字母放到字符串的后面,各个字符的相对位置不变,且不能申请额外的空间。
输入描述: 输入数据有多组,每组包含一个字符串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
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 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
给定一个数组 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
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 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
给定一个包含红色、白色和蓝色,一共 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
给定两个有序整数数组 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
在未排序的数组中找到第 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 ≤ 数组的长度。
思路一:进行排序后再选择(时间复杂度为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
给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 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;
}
}
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 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;
}
}
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 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--;
}
}
}
编写一个函数,以字符串作为输入,反转该字符串中的元音字母。
示例 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);
}
}
给定 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;
}
}
给定一个含有 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
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 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;
}
}
给定一个字符串 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
给定一个字符串 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