什么是算法?
算法简单来说就是解决问题的步骤。
设计原则:正确性、可读性、健壮性 bug、高效率与低存储。内存+CPU内存占用最小,CPU占用最小,运算速度最快。
评价算法的两个重要指标:
时间复杂度:运行一个程序所花费的时间。去掉常数,保留最高阶
一个 while 一个 log(n),一个 for 一个 n
空间复杂度:运行程序所需要的内存,一般就是找数组,容器等
交换两个数,并不一定需要通过第三变量,可以使用加减法
a=2,b=3
a = a + b; //a=5
b = a - b; //b=2
a = a - b; //a=3
public class BubbleSort {
// 4 2 5 1
// 4
// 4 2 => 2 4
// 2 4 5 =
// 2 4 5 1=> 1 2 4 5
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int n = cin.nextInt();
int data[] = new int[n];
for (int i = 0; i < n; i++) {
data[i] = cin.nextInt();
}
// Map size() list.size();
// n = 5
//data 0 1 2 3 4
for (int i = 0, len = data.length; i < len - 1; i++) {
for (int j = 0; j < len - 1 - i; j++) {
if (data[j] > data[j + 1]) {
// 交换 不让你引入第三个变量 交换 a b的值
int temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
}
}
}
for(int i = 0 ; i < n; i++) {
System.out.print(data[i] + " ");
}
System.out.println();
}
}
public class ShellSort {
public static void main(String[] args) {
// 4 1 2 3 5
// 第一步
//len = 5 表示有5个数字
// step = len /2 => 5/2 =2;
// 4 2 =>分出来的还是进行一个插入排序 2 4
// 1 3 => 1 3
// 5=> 5
// 2 4 1 3 5
//100 / 2 = 50
// 50 / 2 => 25
//25 /2
//step = 1排完
// 第二步 step = step / 2 =>1
// 第四步: 1 2 3 4
Scanner cin = new Scanner(System.in);
int n = cin.nextInt();
int data[] = new int[n + 1];
for (int i = 0; i < n; i++) {
data[i] = cin.nextInt();
}
int step = n;
while(step >= 1) {
step = step / 2;
for(int i = step ; i < n; i ++) {
for(int j = i ;j - step >= 0; j-= step) {
if(data[j] < data[j - step]) {
int temp = data[j];
data[j] = data[j - step];
data[j - step] = temp;
}else {
break;
}
}
}
}
for(int i = 0 ; i < n; i++) {
System.out.print(data[i] + " ");
}
System.out.println();
}
}
public class QSort {
public static void qSort(int data[], int left, int right) {
int ll = left;// 从左边找的位置
int rr = right; // 从右边找的位置
int base = data[left]; // 取第一个作为基准数
//Sort
// 1 2 3 4 5
while (ll < rr) {
// 从后面往前找到比基准数小的数进行对换
while (ll < rr && data[rr] >= base) {
rr--;
}
if (ll < rr) { // 为了怕是 没找到
int temp = data[rr];
data[rr] = data[ll];
data[ll] = temp;
ll++;
}
// 从前面往后面找比基准数大的进行对换
while (ll < rr && data[ll] <= base) {
ll++;
}
if (ll < rr) {
int temp = data[rr];
data[rr] = data[ll];
data[ll] = temp;
rr--;
}
}
System.out.println("以Base=" +base+ "的排序结果");
print(data);
// 以基准数分为3部分,左边的比之小,右边比之大 我们要做的就是一把这左边和右边分别进行快速排序
if (ll > left) {
qSort(data, left, ll - 1);
}
if (rr < right) {
qSort(data, ll+1, right);
}
}
public class MergSort {
public static void main(String[] args) {
int data[] = {9,5,6,8,0,3,7,1,20,1};
mergeSort(data, 0, data.length -1);
System.out.println(Arrays.toString(data));
//String a = "1";
//String b = "2";
//a.compareTo(b);
}
public static void mergeSort(int data[],int left,int right) {
if(left < right) {
int mid = (left + right) / 2;
mergeSort(data, left, mid);
mergeSort(data, mid+1, right);
//以上就分完了
merge(data, left, mid, right);
}
}
public static void merge(int data[],int left,int mid,int right) {
int temp[] = new int[data.length]; //就是用来保存合并后的序列,辅助我们排序
int point1 = left; //表示左边的第一个数的位置
int point2 = mid + 1; //表示后边的第一个数的位置
int loc = left; //这个就是用来保存我们当前填了那个数字到temp里面去
while(point1 <= mid && point2 <= right) {
if(data[point1] <= data[point2]) {
temp[loc] = data[point1];
loc ++ ;
point1 ++ ;
}else {
temp[loc] = data[point2];
loc ++ ;
point2 ++ ;
}
}
while(point1 <= mid) {
temp[loc++] = data[point1++];
}
while(point2 <= right) {
temp[loc ++] = data[point2 ++];
}
for(int i = left ;i <= right ; i++) {
data[i] = temp[i];
}
}
}
使用中序遍历来排序
public class BinarySearchTree {
int data;
BinarySearchTree left;
BinarySearchTree right;
public BinarySearchTree(int data) {
this.data = data;
left = null;
right = null;
}
public void insert(BinarySearchTree root,int data) { //构建树
if(root.data < data) { //根结点小于 data 插入右子树
if(root.right == null) {
root.right = new BinarySearchTree(data);
}else { //表示子树不为空 ,那么还要继续往下找,要找到叶子结点才能插入
insert(root.right, data);
}
//insert(root, data);
}else {
if(root.left == null) {
root.left = new BinarySearchTree(data);
}else {
insert(root.left, data);
}
}
}
public void in(BinarySearchTree root) { //中序遍历来排序
if(root != null) {
in(root.left);
System.out.print(root.data + " ");
in(root.right);
}
}
public static void main(String[] args) {
//快速排序,归并排序,二叉树排序
int data[] = {0,5,9,1,2,3,10};
BinarySearchTree root = new BinarySearchTree(data[0]); //第一个点作为跟结点
for(int i = 1 ; i < data.length ; i ++) {
root.insert(root, data[i]);
}
System.out.println("中序遍历:");
root.in(root);
}
}
概念:贪心算法又叫做贪婪算法,它在求解某个问题是,总是做出眼前最大利益。也就是说只顾眼前不顾大局,所以它是局部最优解。
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
题目地址
class Solution {
public int maxProfit(int[] prices) {
//贪心法
if(prices==null || prices.length==0) return 0;
int profit = 0;
for (int i =1;i < prices.length;i++){
if(prices[i]>prices[i-1]) profit+=prices[i]-prices[i-1];
}
return profit;
}
}
分解子问题,通过局部最大值得到全局最大。
思路:构建一个矩阵。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nc5FrVWa-1589633967686)(C:\Users\ccw\Desktop\背包问题.png)]
public class DP {
public static void main(String[] args) {
int value[] = {60,100,120}; //每个物品的钱
int weight[] = {10,20,40}; //每个物品的重量 和上面的一一对应
int w = 50; //袋子的容积
int n = 3; //物品的个数
int dp[][] = new int[n+1][w+1]; //表示分割,成一个 小的表格
for(int i = 1; i<=n ;i++) { //表示物品往里面加
for(int cw = 1; cw <= w; cw ++) { //袋子在每一个容积下所装的最大的钱
if(weight[i - 1] <= cw) { //表示这个物品可以装
dp[i][cw] = Math.max(
value[i-1]+dp[i-1][cw-weight[i-1]], //我装新加的物品
dp[i-1][cw] //我不装这个新加的这个物品
);
}else {
dp[i][cw] = dp[i-1][cw]; //新加的这个装不下 ,那么就取前一个物品装值
}
}
}
System.out.println("袋子能装的最大价值:" + dp[n][w]);
}
}
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
public class Solution {
public int JumpFloorII(int target) {
//有 n 中跳的方式,1阶、2阶...n阶,首次为跳1~n,再把下次跳n-n的可能相加
//如f(3) = f(3-1)+f(3-2)+f(3-3),
//所以f(n) = f(n-1)+f(n-2)+...+f(n-(n-1)) + f(n-n)
//即f(n) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2)+ f(n-1)
//所以 f(n-1) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2)
//通过f(n)-f(n-1)简化得 f(n) = 2*f(n-1)
if (target <= 0) return 0;
if (target <= 1) return 1;
return 2 * JumpFloorII(target - 1);
//解法二:最后一块木板是青蛙到达的位子, 必须存在
//剩下n-1块板,每个木板有存在和不存在两种选择,(n-1) 块木板 就有 [2^(n-1)] 种跳法
//if (target <= 0) return 0;
//return (int) Math.pow(2, target - 1);
}
}
贪心算法&动归的主要使用场景
贪心效率优于动态规划。
贪心是一种特殊的动态规划,只不过他的子问题只会被计算一次。动归则多次计算得到全局最优。
Abc
Acb= >我们把 c变成b b变成c 转换2不可以转换成abc。
这时候我们可以设置阈值,若果转换小于n我们可以认为短语相似。
须存在
//剩下n-1块板,每个木板有存在和不存在两种选择,(n-1) 块木板 就有 [2^(n-1)] 种跳法
//if (target <= 0) return 0;
//return (int) Math.pow(2, target - 1);
}
}
> 贪心算法&动归的主要使用场景
贪心效率优于动态规划。
贪心是一种特殊的动态规划,只不过他的子问题只会被**计算一次**。动归则多次计算得到全局最优。
1. 求解最值问题:可以贪心,查公交最短路线。
2. 短字符相似性匹配:动态规划里面的编辑距离,求字符串变换几次可以相互转换
3. 策略问题:动归,使用矩阵
Abc
Acb= >我们把 c变成b b变成c 转换2不可以转换成abc。
这时候我们可以设置阈值,若果转换小于n我们可以认为短语相似。
> 未完待补充