算法入门

文章目录

  • 算法入门
    • 概述
    • 排序算法
      • 冒泡排序
      • 希尔排序
      • 快速排序
      • 归并排序
      • 二叉树
    • 贪心
    • 动态规划

算法入门

概述

什么是算法?

算法简单来说就是解决问题的步骤。

设计原则:正确性、可读性、健壮性 bug、高效率与低存储。内存+CPU内存占用最小,CPU占用最小,运算速度最快。

评价算法的两个重要指标:

  • 时间复杂度:运行一个程序所花费的时间。去掉常数,保留最高阶

    一个 while 一个 log(n),一个 for 一个 n

    • 常数性时间复杂度 O(1)
    • 线性性 O(n) 单层for
    • 对数性 nlog(n) 快速排序
    • 平方性 O(n^2) 两层for
  • 空间复杂度:运行程序所需要的内存,一般就是找数组,容器等

排序算法

  1. 冒泡排序:其核心就是两两比较,选出大的继续和别人比
  2. 选择排序:拿出来最高的这个 和所有人去比较,得出位置。
  3. 插入排序:对一个有序的序列插入,插入进去后保证还是有序的。
  4. 希尔排序:对插入排序的改进。步长/2,分组,减少比较次数。
  5. 快速排序:使用递归。根据基准数分组,分成两组,左边的都比基准数小,右边都比之大。
  6. 归并排序:先分再合。
  7. 堆排序:是一种选择排序,不稳定排序。堆是一种完全二叉树。参考
  8. 基数排序:将整数按位数切割成不同的数字,然后按每个位数分别比较。参考

交换两个数,并不一定需要通过第三变量,可以使用加减法

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

贪心算法&动归的主要使用场景

贪心效率优于动态规划。

贪心是一种特殊的动态规划,只不过他的子问题只会被计算一次。动归则多次计算得到全局最优。

  1. 求解最值问题:可以贪心,查公交最短路线。
  2. 短字符相似性匹配:动态规划里面的编辑距离,求字符串变换几次可以相互转换
  3. 策略问题:动归,使用矩阵
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我们可以认为短语相似。


> 未完待补充

你可能感兴趣的:(学习笔记)