左右指针
解决问题:数组的相向问题,指针相向移动
引入-- 反转字符串(简单)
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
输入:s = [“h”,“e”,“l”,“l”,“o”]
输出:[“o”,“l”,“l”,“e”,“h”]
限制:1 <= s.length <= 105
class Solution {
public void reverseString(char[] s) {
int n = s.length;
int leftPoint = 0;
int rightPoint = n - 1;
while (leftPoint < rightPoint) {
char temp = s[leftPoint];
s[leftPoint] = s[rightPoint];
s[rightPoint] = temp;
++leftPoint;
--rightPoint;
}
}
}
典型应用-快速排序
思想:分治法
步骤:
第一步:在待排序的n个记录里,任取一个记录,以该记录的排序码为准,将所有的记录都分为两组,第一组都小于该数,第二组都大于该数
第二步:采用相同的方法,对左、右两组分别进行排序,直到所有记录都排到相应的位置为止。
package com.wang.sort;
import java.util.Arrays;
/**
* 快速排序之左右指针<br>
* 选择最左端的为基准值(pivot)<br>
* 从右向左遍历,移动hi指针,找到第一个比基准值小的值。<br>
* 从左向右遍历,移动low指针,找到第一个比基准值大的值。<br>
* 交换此时low和hi指针位置的数值。循环进行,直到low=hi<br>
* low等于hi时,把基准值位置与low位置的数值互换。<br>
* 相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区<br>
* (partition)操作<br>
* 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
* @author wang
* @Date 2018年4月16日
*
*/
public class LeftAndRightPointer {
public static void main(String[] args) {
int[] arrays = {12,21,3,5,2,18,7,4,11,9,12};
System.out.println("快速排序之左右指针排序前:"+Arrays.toString(arrays));
quickSort(arrays , 0 , arrays.length - 1);
System.out.println("快速排序之左右指针排序后:"+Arrays.toString(arrays));
}
public static void quickSort(int[] arrays,int low,int hi){
if(low < hi){
//求每次分治的分割线
int divideIndex = getDivideIndex(arrays,low,hi);
//再递归分别对分割的俩个子数组进行递归排序
quickSort(arrays,low,divideIndex -1);
quickSort(arrays,divideIndex + 1, hi);
}
}
public static int getDivideIndex(int[] arrays,int low,int hi){
//设定arrays[low]为基准值,从右到左移动hi,找到大于第一个大于基准值的数字。
//从左向右移动low指针,找到第一个小于基准值的数,交换low和high位置上的值
//直到low=high的时候,将基准值填入arrays[low]
int baseValue = arrays[low];
int oldLow = low;
while(low < hi){
while (low < hi && arrays[hi] >= baseValue){
hi-- ;
}
while( low < hi && arrays[low] <= baseValue){
low++ ;
}
if( low < hi){
swap(arrays,low ,hi);
}
}
if(low == hi){
arrays[oldLow] = arrays[low];
arrays[low] = baseValue ;
}
return low;
}
public static void swap( int[] arrays ,int low ,int high){
int temp = 0;
temp = arrays[low] ;
arrays[low] = arrays[high] ;
arrays[high] = temp;
}
}
思考题-三数之和
给你一个包含 n 个整数的数组 nums判断 nums 中是否存在三个元素 a,b,c 使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
前后指针(快慢指针)
解决问题:链表类的问题 (一般情况)
引入-环形引入
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null)
return false;
ListNode ps = head;
ListNode pf = head;
while (pf != null && pf.next != null) {
ps = ps.next;
pf = pf.next.next;
if (ps == pf)
return true;
}
return false;
}
}
删除有序数组中的重复项
给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
输入:nums = [1,1,2]
输出:2, nums = [1,2]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
class Solution {
public int removeDuplicates(int[] nums) {
//使用双指针
if(nums==null||nums.length==1){
return nums.length;
}
int i=0,j=1;
while(j<nums.length){
if(nums[i]==nums[j]){
j++;
}else{
i++;
nums[i]=nums[j];
j++;
}
}
return i+1;
}
}
删除有序数组中的重复项II
给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 最多出现两次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
public int removeDuplicates2(int[] nums) {
int i = 0;
int count = 1;
for (int j = 1; j < nums.length; j ++){
if (nums[i] != nums[j]){
nums[++ i] = nums[j];
count = 1;
}else if (count >= 2){
continue;
}else{
nums[++ i] = nums[j];
count ++;
}
}
return i + 1;
}
思考题-接雨水
题目描述:给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下
可以接 6 个单位的雨水(蓝色部分表示雨水)