java刷题随手记 随时更新

一、输入输出

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);
            
            
        }
    }
}

二、基本数据类型

八大基本类型(内置数据类型)

类型位数范围默认值例子
byte8 ( − 128 , 127 ] (-128,127] (128,127]0byte a = 100; byte b = -50;
short
(有符号的以二进制补码表示的整数)
16 ( − 2 15 , 2 15 − 1 ] (-2^{15},2^{15}-1] (215,2151]0short s = 1000;
int32 ( − 2 31 , 2 31 − 1 ] (-2^{31},2^{31}-1] (231,2311]0int a = 1000;
long64 ( − 2 63 , 2 63 − 1 ] (-2^{63},2^{63}-1] (263,2631]0Llong a = 100000L;
float320.0ffloat f1 = 234.5f
double640.0ddouble d1 = 123.4
boolean1true / falsefalseboolean one = true;
char16“\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 接口的,比如 ArrayListLinkedList

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; 
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值