一、输入输出
1.输入
创建输入对象
import java.util.Scanner;
Scanner reader=new Scanner(System.in);
从输入流获取数据
boolean flag = reader.nextBoolean()
byte a = reader. nextByte()
short a = reader.nextShort()
int a = reader.nextInt();
long a = reader.nextLong()
float a = reader.nextFloat()
double a = reader.nextDouble()
String name = reader.nextLine();
//判断是否还有
reader.hasNext();//true有 false没有
//获取一整行并转化为int
int a = Integer.parseInt(in.nextLine());
//获取一整行 转化成String数组,并以空格隔开
String[] str = in.nextLine().split(" ");
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
while(in.hasNext()){
String[] str = in.nextLine().split(" ");
int res = 0;
for(int i = 0; i < str.length; i++){
res += Integer.parseInt(str[i]);
}
System.out.println(res);
}
}
}
import java.util.Scanner;
import java.util.Arrays;
public class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
while(in.hasNext()){
String[] str = in.nextLine().split(",");
Arrays.sort(str);
StringBuffer res = new StringBuffer("");
for(int i = 0; i < str.length; i++){
res.append(str[i]);
res.append(",");
}
res.deleteCharAt(res.length() - 1);
System.out.println(res);
}
}
}
二、基本数据类型
八大基本类型(内置数据类型)
类型 | 位数 | 范围 | 默认值 | 例子 |
---|---|---|---|---|
byte | 8 | ( − 128 , 127 ] (-128,127] (−128,127] | 0 | byte a = 100; byte b = -50; |
short (有符号的以二进制补码表示的整数) | 16 | ( − 2 15 , 2 15 − 1 ] (-2^{15},2^{15}-1] (−215,215−1] | 0 | short s = 1000; |
int | 32 | ( − 2 31 , 2 31 − 1 ] (-2^{31},2^{31}-1] (−231,231−1] | 0 | int a = 1000; |
long | 64 | ( − 2 63 , 2 63 − 1 ] (-2^{63},2^{63}-1] (−263,263−1] | 0L | long a = 100000L; |
float | 32 | 0.0f | float f1 = 234.5f | |
double | 64 | 0.0d | double d1 = 123.4 | |
boolean | 1 | true / false | false | boolean one = true; |
char | 16 | “\u0000”(即0) ~ "\uffff"(即65535) | ‘\u0000’ | char letter = ‘A’; |
数据转换:从低级到高级,然后一起运算
低 ------------------------------------> 高
byte,short,char—> int —> long—> float —> double
判断int是否为偶数
int i = 4;
i & 1 == 0;//偶数
i = 5;
i & 1 == 1;//奇数
if(n ==((n>>1)<<1)){
//是偶数
}else{
//不是偶数
}
三、引用类型
对象(类、接口)、数组都是引用数据类型,所有引用类型的默认值都是null
1、数组
import java.util.Arrays;
int res[];
int[] res;
//创建数组
int[] res = new int[k];
//数组获取
b = res[i];
//复制
Arrays.copyOf(res,count);
//获取数组长度
len = res.length;
//填充
//给数组res每个元素填充1
Arrays.fill(res,1);
//填充nums数组中的[a,b)位置填充1
Arrays.fill(nums,a,b,1);
//一维数组转换成字符串
int[] nums = new int[10];
Arrays.toString(nums);
//char[]转换成字符串
char[] charArray = new int[10];
String str = String.valueOf(charArray);
//二维数组转换成字符串
int[][] nums = new int[10][10];
Arrays.deepToString(nums);
//等价判断(两个对象值相同或者均为同一对象的引用,则返回true)
Arrays.equals(nums1,nums2);
int[] a = new int[10];
int[] b = new int[10];
System.out.println(Arrays.equals(a,b));//true
Arrays.fill(a,1);
System.out.println(Arrays.equals(a,b)); //false
Arrays.fill(a,1);
Arrays.fill(b,1);
System.out.println(Arrays.equals(a,b)); // true
//一维数组排序
Arrays.sort(nums);
//二维数组根据第一列排序
int[][] ts_id = new int[][] {{1,2},{99,0},{876,9},{3,100}};
Arrays.sort(ts_id,new Comparator<int[]>() {
public int compare(int[] o1, int[] o2) {
return o1[0]-o2[0];
}
});
//二维数组根据第二列排序
Arrays.sort(points, new Comparator<int[]>() {
public int compare(int[] point1, int[] point2) {
if (point1[1] > point2[1]) {
return 1;
} else if (point1[1] < point2[1]) {
return -1;
} else {
return 0;
}
}
});
// 新建一个类并排序
public static class Dog implements Comparable<Dog>{
int start;
int end;
int profit;
public Dog(int start, int end, int profit){
this.start = start;
this.end = end;
this.profit = profit;
}
public void print(){
System.out.println(this.start + " " + this.end + " " + this.profit);
}
@Override
public int compareTo(Dog dog){
if(this.end < dog.end){
return -1;//当前对象在前
}else return 1;
}
}
2、字符串
2.1 String
String类被final修饰,所以不可修改。
修改String时产生新的对象,所以多次修改的字符串使用StringBuffer,StringBuilder
public final class String
//创建字符串
String str1 = "hello world!";
String str2=new String("Runoob");
//获取String中的单个字符
str1.charAt(i)
//字符串长度
str.length()
//字符串相等判断(两个对象值相同或者均为同一对象的引用,则返回true)
a.equals(b)
String a = new String("ABD");
String b = new String("ABD");
System.out.println(a.equals(b)); //true
String c = "BCD";
String d = "BCD";
System.out.println(c.equals(d)); //true
//转换成char[]
char[] charArray = str.toCharArray();
2.2 StringBuffer
StringBuffer可不增加对象并修改字符串,线性安全
StringBuilder也是不增加对象修改字符串,但是线性不安全
(1)创建字符串
StringBuffer str = new StringBuffer("ABD");
(2)获取String中的单个字符
str.charAt(i)
(3)字符串长度
str.length()
(4)在末尾添加元素
str.append('x');
(5)删除
//删除[a,b)位置的元素
str.delete(a,b);
//
str.deleteCharAt(i);
(6)转换成String
str.toString();
(7)等价判断
StringBuffer a = new StringBuffer("ABD");
StringBuffer b = new StringBuffer("ABD");
System.out.println(a.equals(b));//false
3.集合框架中的类-- Abstract Collection类
集合包含Set、List、Queue
集合的选择:
需要根据键值获取到元素值时就选用 Map 接口下的集合
需要排序时选择 TreeMap
不排序时就选择 HashMap
需要保证线程安全就选用 ConcurrentHashMap
只需要存放元素值时,就选择实现Collection 接口的集合
需要保证元素唯一时选择实现 Set 接口的集合比如 TreeSet 或 HashSet
不唯一就选择实现 List 接口的,比如 ArrayList 或 LinkedList
3.1AbstractSet
集合,无序性,确定性,唯一性
(1)HashSet && LinkedHashSet
HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合
import java.util.HashSet; // 引入 HashSet 类
//创建对象
HashSet<String> sites = new HashSet<String>();
//添加元素
sites.add("Runoob"); // 重复的元素不会被添加
//判断元素是否存在
sites.contains("Taobao");
//删除元素
sites.remove("Taobao"); // 删除元素,删除成功返回 true,否则为 false
//计算大小
sites.size();
(2)EnumSet
(3)TreeSet
红黑树(自平衡的排序二叉树)
3.2 AbstractList
以下情况使用 ArrayList :
- 频繁访问列表中的某一个元素。
- 只需要在列表末尾进行添加和删除元素操作。
以下情况使用 LinkedList :
- 你需要通过循环迭代来访问列表中的某些元素。
- 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。
(1)LinkedList
LinkedList继承了AbstractSequentialList ,实现了Queue,List,Deque,Cloneable,java.io.Serializable接口
//使用LinkedList--引入类
import java.util.LinkedList;
//使用LinkedList--初始化
LinkedList<String> sites = new LinkedList<String>();
//使用LinkedList--插入元素
sites.addFirst("Wiki");//在头部添加元素
sites.addLast("Wiki"); //在末尾插入元素
sites.add(i, "Wiki"); //在下标为i处插入元素
sites.offerFirst("Wiki");//在头部添加元素,返回是否插入成功true/false
sites.offerLast("Wiki"); //在末尾插入元素,返回是否插入成功true/false
sites.offer("Wiki"); //向链表末尾添加元素,返回是否插入成功true/false
//使用LinkedList--访问元素
sites.getFirst();//获取头部元素
sites.getLast();//获取尾部元素
sites.get(i);//获取下标为i的元素
sites.peek();// 返回第一个元素,返回Null
sites.element();// 返回第一个元素,抛出异常
sites.peekFirst();// 返回第一个元素
sites.peekLast();// 返回尾部元素
//使用LinkedList--修改元素
sites.set(2, "Wiki");
//使用LinkedList--删除元素
sites.removeFirst(); //删除并返回第一个元素
sites.remove();// 删除并返回第一个元素,会抛出异常
sites.removeLast(); // 删除并返回最后一个元素
sites.remove("Wiki"); // 删除某一元素,返回是否成功 true/false
sites.remove(i); // 删除下标为i的元素
sites.poll(); // 删除并返回第一个元素,返回null
//使用LinkedList--计算大小
sites.size();
//使用LinkedList--排序
Collections.sort(sites);//a-->z, 0-->n递增
//使用LinkedList--转换为字符串或者array
String res = sites.toString();//转成字符串,本身没变,返回值为String
// 将ArrayList对象转换成数组,如果ArrayList对象是Integer类型的值,则需要一个一个复制
String[] arr = new String[sites.size()];
sites.toArray(arr);
(2)ArrayList
// 使用--引入类
import java.util.ArrayList; // 引入 ArrayList 类
//使用--初始化
ArrayList<String> sites = new ArrayList<String>(); // 初始化
//使用--插入元素
sites .add(a); //在末尾插入元素a
sites .add(i,a);//在下标i的位置插入元素a
//使用--访问元素
sites.get(1)//访问下标为1的元素
//使用--修改元素
sites.set(2, "Wiki");
//使用--删除元素
sites.remove(3); // 删除下标为3的元素
//使用--计算大小
sites.size();
//使用--排序
Collections.sort(sites);//a-->z, 0-->n递增
//使用--转换为字符串或者array
String res = sites.toString();//转成字符串,本身没变,返回值为String
// 将ArrayList对象转换成数组,如果ArrayList对象是Integer类型的值,则需要一个一个复制
String[] arr = new String[sites.size()];
sites.toArray(arr);
源码–RandomAccess表示可以快速随机访问
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
源码–默认大小是10
private static final int DEFAULT_CAPACITY = 10;
源码–扩容
先使用ensureCapacityInternal()确保容量足够,足够则放入e
如果容量不够,则使用grow()进行扩容,扩到原来的1.5倍,然后复制数组进新的数组
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
源码–删除元素
需要调用 System.arraycopy() 将 index+1 后面的元素都复制到 index 位置上,该操作的时间复杂度为 O(N),可以看到 ArrayList 删除元素的代价是非常高的。
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
(3)vector
Vector 是 List 的古老实现类,底层使用 Object[ ] 存储,线程安全的
3.3AbstractQueue
(1)PriorityQueue
创建堆
PriorityQueue<Integer> queue = new PriorityQueue<Integer>();//默认小顶堆
PriorityQueue<Integer> queue = new PriorityQueue<Integer>((x, y) -> (y - x));//大顶堆
private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(15, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
//取堆顶元素,并删除
queue.poll();
//查看堆顶元素
queue.peek();
//放入元素
queue.offer(a);
//对Person类建堆,实现CompareTo接口来比较大小,年龄小的优先
class Person implements Comparable<Person>{
public int age;
Person(int age){
this.age=age;
}
public int compareTo(Person other){
return age-other.age;
}
}
(2)ArrayQueue
4.集合框架中的类-- AbstractMap类
(1)HashMap && LinkedHashMap
数组作为哈希散列,数组内存储链表,链表内存储结点信息值
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射
HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步
HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口
import java.util.HashMap; // 引入 HashMap 类
//创建实例
HashMap<Integer, String> Sites = new HashMap<Integer, String>();
//添加元素
Sites.put(1, "Google");
//输入键,返回值
String val = Sites.get(3);
//输入键,删除该键对应的键值对
Sites.remove(4);
//删除所有键
Sites.clear();
//计算大小
Sites.size()
// 输出 key 和 value
for (Integer i : Sites.keySet()) {
System.out.println("key: " + i + " value: " + Sites.get(i));
}
// 返回所有 value 值
for(String value: Sites.values()) {
// 输出每一个value
System.out.print(value + ", ");
}
//获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值
countAB.getOrDefault(u, 0)
//检查 hashMap 中是否存在指定的 key 对应的映射关系,返回true/false
countAB.containsKey(u);
(2)TreeMap
(3)EnumMap
(4)WeakHashMap
(5)IdentityHashMap
————————————————————————
5、实例
(1)链表
//结构体定义
public class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
//新建一个结点
ListNode res = new ListNode(-1);
//获取node1结点的下一个结点
a = node1.next;
//获取node1的值
b = node1.val;
//判断是否为空
if(node1 == null){
...
}
(2)栈
//新建栈
Stack<Character> stack = new Stack<>();
//判断栈是否为空
stack.isEmpty()
//放入元素
stack.push('a');
//删除元素
stack.pop();
//获取栈顶元素(不抛出)
stack.peek()
(3)队列
//新建队列
Queue<String> queue = new LinkedList<String>();
//判断栈是否为空
stack.isEmpty();
//放入元素,失败返回false
queue.offer("a");
//删除第一个元素,并返回,失败返回null
queue.poll();
//获取第一个元素(不抛出),失败返回null
queue.peek();
(4)树
1)树的表示
//树的结构体定义
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
2)树的深度优先遍历
1)先序遍历
根-左-右
向左下无限扩展,途中将遍历过的root放入栈中,当某个结点为空时,取栈顶元素的右结点重新开始
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if (root == null) {
return res;
}
Stack<TreeNode> stack = new Stack<TreeNode>();
while(!stack.isEmpty() || root != null){
while(root != null){
res.add(root.val);
stack.push(root);
root = root.left;
}
root = stack.pop();
root = root.right;
}
return res;
}
2)中序遍历
左根右
将所有左结点入栈,直到没有左结点时,
取栈顶元素的值,并取栈顶元素的右结点重新开始
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if (root == null) {
return res;
}
Stack<TreeNode> stack = new Stack<TreeNode>();
while(!stack.isEmpty() || root != null){
while(root != null){//处理所有左结点,进栈
stack.push(root);
root = root.left;
}
if(!stack.isEmpty()){//执行到这里,栈顶元素没有左孩子或者左子树都被访问过
root = stack.pop();
res.add(root.val);
root = root.right;
}
}
return res;
}
3)后序遍历
左右根
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if (root == null) {
return res;
}
TreeNode prev = null;
Stack<TreeNode> stack = new Stack<TreeNode>();
while(!stack.isEmpty() || root != null){
while(root != null){
stack.push(root);
root = root.left;
}
root = stack.pop();
if (root.right == null || root.right == prev) {
res.add(root.val);
prev = root;
root = null;
} else {
stack.push(root);
root = root.right;
}
}
return res;
}
3)二叉平衡树
左旋
四、算法
1、排序
2、查找
(1)顺序查找
输入数组和值,返回该值的下标
(2)二分查找
在有序数组中进行二分查找
运行时间:
O
(
log
2
n
)
O(\log_2 n)
O(log2n)
A.递归方法:
public static int binarySearch(int[] arr, int left, int right, int findval) {
if (left > right) {
return -1;
}
int mid = (left + right) / 2;
if (arr[mid] < findval) {
return binarySearch(arr, mid + 1, right, findval);
} else if (arr[mid] > findval) {
return binarySearch(arr, left, mid - 1, findval);
} else {
return mid;
}
}
B.非递归:
public static int binarySearch(int[] arr, int num) {
int left = 0;
int right = arr.length - 1;
int mid;
while (right >= left) {
mid = (int) (right + left) / 2;
if (arr[mid] == num) {
return mid;
} else if (arr[mid] > num) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
(3)插值查找
public static int insertSearch(int[] arr, int left, int right, int findval) {
if (left > right) {
return -1;
}
int mid = left + (right - left) * (findval - arr[left]) / (arr[right] - arr[left]);
if (arr[mid] < findval) {
return binarySearch(arr, mid + 1, right, findval);
} else if (arr[mid] > findval) {
return binarySearch(arr, left, mid - 1, findval);
} else {
return mid;
}
}
(4)黄金分割查找(斐波那契查找)
//非递归方法得到一个斐波那契数列
public static int[] fib() {
//[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
int[] f = new int[maxSize];
f[0] = 1;
f[1] = 1;
for (int i = 2; i < maxSize; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f;
}
public static int fibSearch(int[] a, int key) {
int low = 0;
int high = a.length - 1;
int k = 0; //表示斐波那契分割数值的下标
int mid = 0; //存放mid值
int f[] = fib(); //获取到斐波那契数列
//获取到斐波那契分割数值的下标
while(high > f[k] - 1) {
k++;
}
//因为 f[k] 值 可能大于 a 的 长度,因此我们需要使用Arrays类,构造一个新的数组,并指向temp[]
//不足的部分会使用0填充
int[] temp = Arrays.copyOf(a, f[k]);
//实际上需求使用a数组最后的数填充 temp = {1,8, 10, 89, 1000, 1234, 0, 0} => {1,8, 10, 89, 1000, 1234, 1234, 1234,}
for(int i = high + 1; i < temp.length; i++) {
temp[i] = a[high];
}
// 使用while来循环处理,找到我们的数 key
while (low <= high) { // 只要这个条件满足,就可以找
mid = low + f[k - 1] - 1;
if(key < temp[mid]) { //我们应该继续向数组的前面查找(左边)
high = mid - 1;
k--;
} else if ( key > temp[mid]) { // 我们应该继续向数组的后面查找(右边)
low = mid + 1;
k -= 2;
} else { //找到
//需要确定,返回的是哪个下标
if(mid <= high) {
return mid;
} else {
return high;
}
}
}
return -1;
}
3、分治
将复杂问题分为多个小问题,小问题的解可以直接求解,然后合并小问题的解得到复杂问题的解。如排序算法(快速排序、归并排序),傅立叶变换等问题
(1)汉诺塔问题:
public static void hanoi(int n,List<Integer> A, List<Integer> B, List<Integer> C){
if(n == 1){
C.add(A.get(A.size()-1));
A.remove(A.size() - 1);
}else{
//A移到B
hanoi(n-1,A,C,B);
//A移到C
C.add(A.get(A.size()-1));
A.remove(A.size() - 1);
//B移到C
hanoi(n-1,B,A,C);
}
}
4、动态规划
借助表格,利用表格中已计算的值,来计算当前值,且当前值不再更新,作为后续计算依据
例题:leedcode62,leedcode63,leedcode64
(1)leedcode 62. 不同路径
题目描述:从 m x n 网格的左上角到网格的右下角,每次只能向下或者向右移动一步,问总共有多少条不同的路径?
算法思想:填表如3*7的网格
0|1|2| 3| 4| 5| 6
0||1|1|1| 1| 1| 1| 1
1||1|2|3| 4| 5| 6| 7
2||1|3|6|10|15|21|28
实现:
public int uniquePaths(int m, int n) {
if(m==0||n==0)return 1;
int[][] arrayset = new int[n][m];
for(int i = 0; i < n;i++){
for(int j = 0;j < m;j++){
if(i==0 || j ==0){
arrayset[i][j] = 1;
}else{
arrayset[i][j] = arrayset[i-1][j] + arrayset[i][j-1];
}
}
}
return arrayset[n-1][m-1];
(2)leedcode 63. 不同路径 II
题目描述:从 m x n 网格的左上角到网格的右下角,每次只能向下或者向右移动一步,中间某些网格有障碍物,问总共有多少条不同的路径?
初始表格:
0|1|2
0||0|0|0
1||0|1|0
2||0|0|0
算法思想:填表如3*7的网格,依次填表第一行与第一列,然后根据网格的左边和上面格子计算
令不可到达为-1,其余值为路径数
如果均无障碍物则是两者相加,
若有不可到达的不计算不可到达的路径,只算可到的路径数
0| 1|2|
0||1| 1|1|
1||1|-1|1|
2||1| 1|2|
5、KMP算法
应用场景:字符串匹配,在串A中查找串B的起始位置
算法思想:利用之前判断过的信息,通过next数组保存模式串中前后最长公共子序列的长度,每次回溯时通过next数组找到。
(1)字符串匹配
例题:leedcode28. 实现 strStr()
//题目:给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
//思路:先对子串进行部分匹配值表格的填写,然后与haystack串比较扫描时向后移动的位数,通过最大公共前缀来减少回溯
//移动位数=已匹配的字符数-已匹配字符串的最大公共前缀(详见https://www.bilibili.com/video/BV1E4411H73v?p=162)
class Solution {
public int strStr(String haystack, String needle) {
if(needle.length() ==0)return 0;
if(haystack.length()==0)return -1;
int[] next=KMPNext(needle);
return KMP(haystack,needle,next);
}
//输入字符串与待查找子串,返回子串在字符串中第一次出现的位置,没有则返回-1
//每次不匹配的时候,str1的下标向后移动 = 已匹配数-当前匹配的最大公共前缀
public int KMP(String str1,String str2,int[] next){
for(int i = 0,j = 0; i< str1.length(); i++){
while(j > 0 && str1.charAt(i) != str2.charAt(j)){
j = next[j-1];
}
if(str1.charAt(i) == str2.charAt(j)){
j++;
}
if(j == str2.length()){
return i-j+1;
}
}
return -1;
}
//求前缀与后缀的最长公共长度
//对循环到i的字符串,寻找其前n位和后n位最大重复的长度
// 如abcdabd i=5时,指向第2个b,其最大的n则是2,即ab子串,则next[5]=2
private static int[] kmpNext(String dest) {
int[] next = new int[dest.length()];
next[0] = 0;
for (int i = 1, j = 0; i < dest.length(); i++) {
while (j > 0 && dest.charAt(j) != dest.charAt(i)) {//j>0表示前面已经有匹配的,但是当前字符串不匹配,所以对j循环回溯找到匹配的点
j = next[j - 1];
}
if (dest.charAt(j) == dest.charAt(i)) {//找更长的匹配项
j++;
}
next[i] = j;
}
return next;
}
}
6、贪心算法
例题:leedcode 55. 跳跃游戏:给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。
思想:设置flag标记该位置是否可达,遍历数组,将其范围内的位置都标记为可达,过程中对标记过程进行剪枝,最后检查flag里面是否有不可达的位置
public boolean canJump(int[] nums) {
boolean[] flag = new boolean[nums.length+1];
if(nums[0] >= 0)flag[0] = true;
int start;
int end = 0;
int min = 0;
int j;
for(int i = 0; i< nums.length; i++){
if(i+1 < min){//添入了剪枝
start = min;
}else{
start = i+1;
}
if (i + nums[i] < nums.length){
end = i + nums[i];
}else{
end = nums.length;
}
for(j=start; j <= end; j ++){
flag[j] = true;
}
min = end;
}
for(int i = 0; i< nums.length; i++){
if(!flag[i]){
return false;
}
}
return true;
}