找到数组中两数和为目标值的数,返回计数位置
双指针单向双层逐个遍历:182ms
class Solution {
public int[] twoSum(int[] numbers, int target) {
for (int i = 0; i < numbers.length; i++) {
for (int j = i + 1; j < numbers.length; j++) {
if(target == numbers[i] + numbers[j])return new int[]{
i + 1,j + 1};
}
}
return null;
}
}
双指针法不讨论
运用hash表:2ms
class Solution {
public int[] twoSum(int[] numbers, int target) {
HashMap<Integer, Integer> map = new HashMap<>();
int[] arr = new int[2];
for (int i = 0; i < numbers.length; i++) {
if(map.containsKey(target - numbers[i])){
return new int[]{
map.get(target - numbers[i]) + 1,i + 1};
}
map.put(numbers[i],i);
}
return null;
}
}
双指针单层双向遍历:0ms
class Solution {
public int[] twoSum(int[] numbers, int target) {
if(numbers == null)return null;
int i = 0,j = numbers.length - 1;
while (i < j) {
int sum = numbers[i] + numbers[j];
if(sum > target){
j--;
} else if (sum < target) {
i++;
}else {
return new int[]{
i + 1,j + 1};
}
}
return null;
}
}
★总结:
是否存在一个数,是两个数的平方的和
运用sqrt函数,即取根,为了方式数据溢出,取一个数的平方要定义成long型
class Solution {
public boolean judgeSquareSum(int c) {
for (long a = 0; a * a <= c; a++) {
double b = Math.sqrt(c - a * a);
if(b == (int) b){
return true;
}
}
return false;
}
}
双指针双向减小范围验证是否是平方和数
class Solution {
public boolean judgeSquareSum(int c) {
long a = 0;
long b = (long) Math.sqrt(c - a * a);
while (a <= b) {
long sum = a * a + b * b;
if (sum > (long) c) {
b--;
}else if(sum < (long) c){
a++;
}else {
return true;
}
}
return false;
}
}
先假设两个数,一大一小,计算它们的平方和数
不断减小区间至两数相等,如果存在他们的和等于形参,说明是对的
class Solution {
public String reverseVowels(String s) {
if(s == null)return null;
char[] carr = s.toCharArray();
long a = 0;
long b = carr.length - 1;
while (a < b) {
if(isYuan(carr[(int) b]) && isYuan(carr[(int) a])){
char tmp = carr[(int) b];
carr[(int) b] = carr[(int) a];
carr[(int) a] = tmp;
a++;
b--;
}else if(isYuan(carr[(int) a]) && !isYuan(carr[(int) b])){
b--;
}else if(!isYuan(carr[(int) a]) && isYuan(carr[(int) b])){
a++;
}else {
a++;
b--;
}
}
return new String(carr);
}
public boolean isYuan(char c){
if(c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' ||
c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U')return true;
return false;
}
}
思路:
给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。
可以删除一个字符,判断是否能构成回文字符串。
class Solution {
public boolean validPalindrome(String s) {
for (int i = 0,j = s.length() - 1; i < j; i++,j--) {
if(s.charAt(i) != s.charAt(j)){
//当前不等没关系但是后面要是有一个是回文串,就说明符合题意,如abac
return isvalidPalindrome(s,i,j - 1) || isvalidPalindrome(s,i + 1,j);
}
}
return true;//对应的字符都相等了,出来的就是回文串
}
public boolean isvalidPalindrome(String s,int b,int e){
while (b < e) {
if (s.charAt(b++) != s.charAt(e--)) {
return false;
}
}
return true;
}
}
①合并后排序但是面试时属于最差解法
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
for (int i = m,j = 0; i < nums1.length && j < nums2.length; i++,j++) {
nums1[i] = nums2[j];
}
Arrays.sort(nums1);
}
}
②常规双指针
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int[] arr = new int[m + n];
int i1 = 0,i2 = 0;
int cur;
while (i1 < m || i2 < n) {
if(i1 == m){
//索引到达数组1的尾部
cur = nums2[i2++];
}else if(i2 == n){
//索引到达数组2的尾部
cur = nums1[i1++];
}else if(nums1[i1] < nums2[i2]){
cur = nums1[i1++];
}else {
cur = nums2[i2++];
}
arr[i1 + i2 - 1] = cur;
}
for (int i = 0; i < m + n; i++) {
nums1[i] = arr[i];
}
}
}
思路:
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int tail = nums1.length - 1;//尾索引
int i1 = m - 1;
int i2 = n - 1;
while (i2 >= 0) {
if(i1 < 0 || nums1[i1] <= nums2[i2]){
nums1[tail--] = nums2[i2--];
}else {
nums1[tail--] = nums1[i1--];
}
}
}
}
思路:
观察可知, nums1的后半部分是空的, 可以直接覆盖而不会影响结果, 所以可以将指针设置为从后向前遍历, 每次取两者之中的较大者放进nums1的最后面
给定一个链表,判断链表中是否有环。
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null || head.next == null)return false;
ListNode fast = head;
ListNode slow = head;
while (fast != null) {
fast = fast.next;
if (fast != null) {
fast = fast.next;
}
if (fast == slow) {
return true;
}
slow = slow.next;
}
return false;
}
}
思路:
定义快慢指针,快指针走的速度是慢指针的两倍,如果两个指针从起点走,最终还能遇到的话说明链表存在环
给你一个字符串 s 和一个字符串数组 dictionary ,找出并返回 dictionary 中最长的字符串,该字符串可以通过删除 s 中的某些字符得到。
class Solution {
public String findLongestWord(String s, List<String> dictionary) {
int n = s.length();
String ans = "";
for (String sl : dictionary) {
int m = sl.length();
int p = 0, q = 0;
while (p < n && q < m) {
if (s.charAt(p) == sl.charAt(q)) {
q++;
}
p++;
}
if (q == sl.length()) {
if (sl.length() == ans.length()) {
ans = sl.compareTo(ans) < 0 ? sl : ans;
} else {
ans = sl.length() > ans.length() ? sl : ans;
}
}
}
return ans;
}
}
问题:
思路:
核心总结
双指针的思想就是建立两个指针,这两个指针可以使相同方向,一般前进的速度不同或者两者的前进顺序不一致;也可能是相反的方向,通过使用相关的变量控制来达到我们的目的。
1.判断有无环:快指针一步走两个,慢指针一步一个,如果有环,最后指针会相遇;如果无环快指针先遇到null;
2.快慢指针可以寻找链表的中点(左右指针也可以)
3.相差问题,寻找链表的倒数第k个元素
快指针先走k步,然后快慢指针同时同速前进,当快指针遇到null时,慢指针到达倒数第k个节点。
1.二分查找、有序的两数之和、反转数组
2.快速排序
思路:
将每个元素逐个插入,先将先记录有序表的最后一个数,用无序表的第一个数与有序表的每个元素逐个比较。如果无序表中的拿到的数大于有序表中第一个数的话,就进行互换
public static void InsertSort1(int[] arr) {
int temp,i,j;
for (i = 1; i < arr.length; i++) {
//待插入元素从第二个数开始
for (j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
//在满足指针非空且当前数大于前一个数时,全部一个个交换
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
public static void shellSort(int[] arr){
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
//分gap组
for (int i = gap; i < arr.length; i++) {
//在gap确定时,分小组
for (int j = i - gap; j >= 0; j -= gap) {
//比较每一小组的两个数的大小
if(arr[j] > arr[j + gap]){
int temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
}
}
}
先找出数组中最小的,拿最小的和第一个值比较,小的放到最前面
public static void selectSort(int[] arr){
for(int i = 0; i < nums.length - 1; i++) {
//遍历长度-1次
for(int j = i + 1; j < nums.length; j++) {
if(nums[i] > nums[j]) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}
}
基于对这种数据结构,即根节点的值大于所有孩子结点的值。堆排序的两大步骤:
①将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆
②将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端
③重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序
public static void adjustheap(int[] arr, int i,int length){
int temp = arr[i];//先取出当前元素的值,保存在临时变量
for (int k = 2*i + 1; k < length; k=2*k+1) {
//k = i * 2 + 1 k 是 i节点的左子节点
if(k+1 < length && arr[k] < arr[k+1]){
//如果左子节点小于右子节点,就把指针指到右子节点上
k++;//即k++
}
if(temp < arr[k]){
//此时k在右子节点上,如果右子节点的数大于当前的节点的数
arr[i] = arr[k];//就把右子节点(大数)给到子树根(也就是一开始的当前节点,小数)
i = k;//指针i此时指在右子节点上
}else{
break;//如果子节点比根节点小,就不管,不操作
}
arr[i] = temp;//此时将小数给右子节点,完成互换
}
}
/**
* 功能:完成将以i对应的非叶子结点的树调整成大顶堆
* @param arr 待调整的数组
* @param i 表示非叶子结点在数组中索引
* @param length 表示对多少个元素继续调整, length是在逐渐的减少
*/
public static void heapsort(int[] arr){
int temp = 0;
for (int i = arr.length / 2 - 1; i >= 0; i--) {
//大顶堆的构建要经历(非叶子节点的个数)次
adjustheap(arr,i,arr.length);
}
for (int i = arr.length - 1; i > 0; i--) {
//一共排序要经历(数组长度-1)次
temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;//此时把大顶堆上的大数和数组的第一个(最小的数)互换
adjustheap(arr,0,i);
}
}
思路:
public static void bubble(int []arr){
int temp = 0;
for (int i = 0; i < arr.length - 1; i++) {
//经历长度-1次
for (int j = 0; j < arr.length - 1 - i; j++) {
//在去掉i的区间里交换数即可
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
public static void quicksort(int arr[], int left, int right) {
int l = left;//左指针
int r = right;//右指针
int pivot = arr[(left + right) / 2];//基准值
int temp = 0;
while (l < r) {
while (arr[l] < pivot) {
//左指针对应数小于基准值
l += 1;//左指针右移
}
while (pivot < arr[r]) {
//右指针对应数大于基准值
r -= 1;//右指针左移
}
if(l>=r){
//如果移着移着,左指针大于等于右指针,直接结束
break;
}
temp = arr[r];//左右指针指针移动完之后,进行数据交换
arr[r] = arr[l];
arr[l] = temp;
if (arr[r] == pivot) {
//如果此时的数和基准值相同
l += 1;
}
if (arr[l] == pivot) {
r -= 1;
}
}
// 如果 l == r, 必须l++, r--, 否则为出现栈溢出
if (l == r) {
r -= 1;
l += 1