《算法基础》简单算法

article/2025/11/10 10:05:14

《acwing算法基础》简单算法

文章目录

  • 《acwing算法基础》简单算法
    • 快速排序:
      • 思路:
      • 时间复杂度:
      • 代码:
      • 快排边界问题:
      • 练习题:
    • 归并排序:
      • 思路:
      • 时间复杂度:
      • 代码:
      • 练习题:
    • 二分思想:
      • 思路:
      • 时间复杂度:
      • 代码:
      • 练习题:
    • 一维前缀合与二维前缀和:
    • 差分:
    • 双指针算法:
      • 模板:
      • 练习题:
    • 二进制算法:
    • 离散化:
      • 模板:
    • 区间合并:
      • 练习题:
    • 技巧:
      • 输出有效位浮点数:
      • 判断重复:
      • 对List类进行排序的:

快速排序:

思路:

  1. 给定一个无序的数组.

  2. 选定一个基准值x(建议数组的中间数字),一个指针left,一个指针right,left指向第一个元素,right指向第二个元素.,保证left下标永远小于等于right下标的前提下,如果数组left下标的值小于基准值数字,则left++,直到找到arr[left]>=x,如果数组right下标的值大于基准值数字,则right–,直到找到arr[right]<=x,将这两个元素进行交换.循环进行,直到基准值左下标的值小于等于x,基准值右下标的值大于等于x.

  3. 递归左区间与右区间.

时间复杂度:

O(nlogn)

代码:

import java.util.Scanner;
public class Main{public static int[] arr = new int[1000010];public static void quick_sort(int[] arr,int l,int r) {if(l>=r){return;}int i = l-1;int j = r+1;             int q = arr[l+r>>1];        //基准值(用于比较)while(i<j){do{i++;}while(arr[i]<q);do{j--;}while(arr[j]>q);if(i<j){int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;}}quick_sort(arr,l,j);quick_sort(arr,j+1,r);}public static void main(String[] args){Scanner sc = new Scanner(System.in);int n = sc.nextInt();for(int i = 0;i < n;i++){arr[i] = sc.nextInt();}quick_sort(arr,0,n-1);for(int i = 0;i < n;i++){System.out.print(arr[i]+" ");}}

在代码中,基准值选择l或者r,都会容易出现边界问题.所以建议以中间的元素为基准值,确保不会有边界问题的出现

快排边界问题:

  • 第一种情况:

  • 在这里插入图片描述

  • 第二种情况:

  • 在这里插入图片描述

练习题:

给定一个长度为 n 的整数数列,以及一个整数 k,请用快速选择算法求出数列从小到大排序后的第 k 个数。

归并排序:

思路:

  1. 确定分界点:一般以中间为界限

  2. 进行递归排序

  3. 归并

在这里插入图片描述

时间复杂度:

n(logn)

是一个稳定的排序算法

代码:

import java.util.Scanner;
public class Main{public static int[] arr = new int[1000010];public static int[] tmp = new int[1000010];public static void marge_sort(int[] q,int l,int r){if(l>=r){return;}int mid = l+r>>1;marge_sort(q,l,mid);marge_sort(q,mid+1,r);int k = 0;int i = l;int j = mid+1;while(i<=mid&&j<=r){if(q[i]<q[j]){tmp[k] = q[i];k++;i++;}else{tmp[k] = q[j];k++;j++;}}while(i<=mid){tmp[k] = q[i];i++;k++;}while(j<=r){tmp[k] = q[j];k++;j++;}for(i = l,j=0;i<=r;i++,j++){arr[i] = tmp[j];}}public static void main(String args[]){Scanner sc = new Scanner(System.in);int n = sc.nextInt();for(int i =0;i<n;i++){arr[i] = sc.nextInt();}marge_sort(arr,0,n-1);for(int i = 0;i < n;i++){System.out.print(arr[i]+" ");}}
}

练习题:

给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。

逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i<j 且 a[i]>a[j],则其为一个逆序对;否则不是。

输入格式

第一行包含整数 n,表示数列的长度。

第二行包含 n 个整数,表示整个数列。

输出格式

输出一个整数,表示逆序对的个数。

二分思想:

思路:

在我们之前对于二分的理解中,二分用于对有序数组的查询,达到时间复杂度为O(logn)的算法.

但是二分其实不仅仅只适用于有序数组,在满足一定条件的数组或者字符串一样可以成立.

时间复杂度:

O(logn)

代码:

Boolean check(int x) {/* ... */} // 检查x是否满足某种性质// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{while (l < r){int mid = l + r >> 1;// check()判断mid是否满足性质//当题目中有多个答案时,这个模板可以求的序列满足条件的第一个答案.if (check(mid)) r = mid;    else l = mid + 1;}return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{while (l < r){int mid = l + r + 1 >> 1;//当题目中有多个答案时,这个模板可以求的序列满足条件的最后一个答案.if (check(mid)) l = mid;else r = mid - 1;}return l;
}
//浮点数二分:
bool check(double x) {/* ... */} // 检查x是否满足某种性质double bsearch_3(double l, double r)
{const double eps = 1e-6;   // eps 表示精度,取决于题目对精度的要求while (r - l > eps){double mid = (l + r) / 2;if (check(mid)) r = mid;else l = mid;}return l;
}

练习题:

给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。

对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。

如果数组中不存在该元素,则返回 -1 -1

import java.util.*;
public class Main{public static void main(String avgs[]){Scanner scan = new Scanner(System.in);int n = scan.nextInt();int m = scan.nextInt();int[] array = new int[n];for(int i = 0;i < n;i++){array[i] = scan.nextInt();}while(m!=0){int x = scan.nextInt();int l = 0;int r = n-1;while(l<r){int mid = (l+r)/2;if(array[mid] >= x){r = mid;}else{l = mid+1;}}if(array[l] != x){System.out.println("-1 -1");m--;}else{System.out.printf(l+" ");l = 0;r = n-1;while(l<r){int mid = (l+r+1)/2;if(array[mid] <= x){l = mid;}else{r = mid-1;}}System.out.println(l);m--;}}}
}

对于第一个模版,可以得到序列中满足x的第一个值,对于第二个模版,可以得到序列中满足x的最后一个值.

一维前缀合与二维前缀和:

通过前缀合可以得到数组0-x的值.

一维:

S[i] = a[1] + a[2] + ... a[i]
a[l] + ... + a[r] = S[r] - S[l - 1]

二维:

//第i行j列格子左上部分所有元素的和
//以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:S[i, j] = S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]

差分:

在构造差分数组时,一样使用insert直接插入就能得到差分数组.

通过差分可以以O(1)的复杂度为数组[l-r]的数字都加上每一个数.

一维:

给区间[l, r]中的每个数加上c:
B[l] += c, B[r + 1] -= c

二维:

给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:
S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c

二维矩阵练习题:

输入一个n行m列的整数矩阵,再输入q个操作,每个操作包含五个整数x1, y1, x2, y2, c,其中(x1, y1)和(x2, y2)表示一个子矩阵的左上角坐标和右下角坐标。

每个操作都要将选中的子矩阵中的每个元素的值加上c。

请你将进行完所有操作后的矩阵输出。

BufferReader输入为什么比Scanner快?:

  • BufferedReader相对于Scanner来说要快一点,因为Scanner对输入数据进行正则解析,而BufferedReader只是简单地读取字符序列

详解参考大佬的解释: (卡了Scanner的输入导致超时,不看大佬的真的debug不出来.)

AcWing 798. 【Java】差分矩阵 - AcWing

import java.io.*;public class Main {private static int N = 1010;private static int M = 1010;private static int[][] a = new int[N][M];private static int[][] b = new int[N][M];public static void main(String[] args) throws Exception{BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));String[] str = reader.readLine().split(" ");int n = Integer.parseInt(str[0]);int m = Integer.parseInt(str[1]);int q = Integer.parseInt(str[2]);for(int i = 1; i < n + 1; i++){str = reader.readLine().split(" ");for(int j = 1; j < m + 1; j++){//这里str下标是j-1a[i][j] = Integer.parseInt(str[j - 1]);//构造差分矩阵binsert(b,i,j,i,j,a[i][j]);}}while(q-- > 0){str = reader.readLine().split(" ");int x1 = Integer.parseInt(str[0]);int y1 = Integer.parseInt(str[1]);int x2 = Integer.parseInt(str[2]);int y2 = Integer.parseInt(str[3]);int c = Integer.parseInt(str[4]);insert(b,x1,y1,x2,y2,c);}for(int i = 1; i < n + 1; i++){for(int j = 1; j < m + 1; j++){//求矩阵前缀和//这里写+=后面就不用+b[i][j]了,否则是双倍b[i][j] = b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1] + b[i][j];writer.write(b[i][j] + " ");}writer.write("\n");}writer.flush();writer.close();reader.close();}public static void insert(int[][] b, int x1, int y1, int x2, int y2, int c){b[x1][y1] += c;b[x1][y2 + 1] -= c;b[x2 + 1][y1] -= c;b[x2 + 1][y2 + 1] += c;}
}

双指针算法:

模板:

for (int i = 0, j = 0; i < n; i ++ )
{while (j < i && check(i, j)) j ++ ;// 具体问题的逻辑
}

练习题:

在这里插入图片描述

答案:

import java.util.*;
public class Main{public static void main(String[] args){Scanner scan = new Scanner(System.in);int n = scan.nextInt();int[] arr = new int[n];int[] s = new int[100010];for(int i = 0;i<n;i++){arr[i]=scan.nextInt();}int m = 0;for(int i = 0,j = 0;i < n;i++){s[arr[i]]++;while(j<i&&s[arr[i]]>1){s[arr[j]]--;j++;}m = Math.max(m,i-j+1);}System.out.println(m);}
}

二进制算法:

求n的第k位数字: n >> k & 1
返回n的最后一位1lowbit(n) = n & -n

离散化:

主要用于数据值大,但数据量稀疏(少)

模板:

public static int Unique(List<Integer> list){int j = 0;for(int i = 0;i < list.size();i++){if(i==0||list.get(i)!=list.get(i-1)){list.set(j,list.get(i));j++;}}return j;}
//进行离散化Collections.sort(alls); //对alls 的所有数都进行排序int unique = Unique(alls); //对alls进行去重并记录下最后的下标.(其实就是重复的数字都放在了unique下标之后)alls = alls.subList(0,unique);//拿到unqiue之前所有的数
public static int find(int x,List<Integer> list){int l = 0;int r = list.size()-1;while(l<r){int mid = l+r>>1;if(list.get(mid)>=x){r = mid;}else{l = mid+1;}}return l+1;  //看题目要求 
}

练习:

在这里插入图片描述

import java.util.*;
class pair{public int first = 0;public int second = 0;pair(int a,int b){first = a;second = b;}}
public class Main{public static int find(int x,List<Integer> list){int l = 0;int r = list.size()-1;while(l<r){int mid = l+r>>1;if(list.get(mid) >= x){r = mid;}else{l = mid + 1;}}return l + 1;}public static int Unique(List<Integer> list){int j = 0;for(int i = 0;i < list.size();i++){//千万别写成while了.if(i==0||list.get(i)!=list.get(i-1)){list.set(j,list.get(i));j++;}}return j;}public static void main(String[] args){Scanner scan = new Scanner(System.in);int n = scan.nextInt();int m = scan.nextInt();int T = 300010;//存储 x,l,r  n+2m <= 300000int[] a = new int[T];//作为存储数据的数组int[] s = new int[T];//作为前缀和的数组List<Integer> alls = new ArrayList<>();//存储所有操作数 x,l,r (这份数据需要进行离散化的,将每个数据都离散化为顺序表的坐标)List<pair> add = new ArrayList<>();//存储 x和cList<pair> option = new ArrayList<>();//存储操作数 l,rfor(int i = 0; i < n;i++){int x = scan.nextInt();int c = scan.nextInt();add.add(new pair(x,c));alls.add(x);}for(int i = 0;i < m;i++){int l = scan.nextInt();int r = scan.nextInt();option.add(new pair(l,r));alls.add(l);alls.add(r);}//进行离散化Collections.sort(alls); //对alls 的所有数都进行排序int unique = Unique(alls); //对alls进行去重并记录下最后的下标.(其实就是重复的数字都放在了unique下标之后)alls = alls.subList(0,unique);//拿到unqiue之前所有的数for(pair p:add){int index = find(p.first,alls);a[index] += p.second; }//构造前缀和for(int i = 1; i <= alls.size();i++){s[i] = s[i-1]+a[i];}//m次询问for(pair p:option){int l = find(p.first,alls);int r = find(p.second,alls);System.out.println(s[r]-s[l-1]);}}
}

区间合并:

模板:

 public static List<pair> mearge(List<pair> list){//对 list集合进行自定义排序.Collections.sort(list,new Comparator<pair>(){public int compare(pair p1,pair p2){return p1.l - p2.l;}  });List<pair> ret = new ArrayList<>();int st = (int)-2e9;int ed = (int)-2e9;for(int i = 0;i < list.size();i++){if(ed < list.get(i).l){if(st != -2e9){ret.add(new pair(st,ed));}st = list.get(i).l;ed = list.get(i).r;}else{ed = Math.max(ed,list.get(i).r);}}if(st!=-2e9){ret.add(new pair(st,ed));}return ret;}

练习题:

在这里插入图片描述

import java.util.*;
class pair{public int first = 0;public int second = 0;public pair(int a ,int b){first = a;second = b;}
}
public class Main{public static List<pair> result(List<pair> list){Collections.sort(list,new Comparator<pair>(){public int compare(pair p1,pair p2){return p1.first - p2.first;}});int st = (int)-2e9;int ed = (int)-2e9;List<pair> ret = new ArrayList<>();for(int i = 0;i < list.size();i++){if(ed<list.get(i).first){//第一次不能加入if(st!=-2e9){ret.add(new pair(st,ed));}st = list.get(i).first;ed = list.get(i).second;}else{ed = Math.max(ed,list.get(i).second);}}if(st!=-2e9){ret.add(new pair(st,ed));}return ret;}public static void main(String[] args){Scanner scan = new Scanner(System.in);int n = scan.nextInt();List<pair> list = new ArrayList<>();for(int i = 0;i < n;i++){int l = scan.nextInt();int r = scan.nextInt();list.add(new pair(l,r));}list = result(list);System.out.println(list.size());}
}

技巧:

输出有效位浮点数:

        System.out.println(String.format("%.6f", l));

判断重复:

使用数组.

799. 最长连续不重复子序列 - AcWing题库

使用hashmap

对List类进行排序的:

//注意 ,里面没有实现list的泛型类排序,需要自己引入comparator.(比较器)
Collections.sort(list);

http://chatgpt.dhexx.cn/article/DhpPOBAG.shtml

相关文章

算法(基础)

算法基础 学习网址排序线性表顺序表链表栈与队列栈队列贪心法分治法搜索法滤波哈夫曼编码与译码B站视频csdn博客算法是编程的灵魂 学习网址 https://www.runoob.com/排序 冒泡排序分析

算法基础课-基础算法

基础算法 第一章 基础算法1快速排序2归并排序3二分算法整数二分浮点二分 4高精度高精度加法高精度减法高精度乘法&#xff08;一个高精度乘正常整数&#xff09;高精度除法&#xff08;一个高精度除以正常整数&#xff09; 5前缀和一维前缀和二维前缀和 6差分一维差分二维差分 …

【算法基础】基础算法

快速排序 模板题&#xff1a;785. 快速排序 - AcWing题库 思路&#xff1a; 定义一个x&#xff08;一般喜欢用中间的&#xff09;&#xff0c;我们快速排序&#xff0c;让x左边的都比它小&#xff0c;同时让右边的都比它大。然后像二分一样不断细分&#xff0c;缩小范围进行同…

数据结构与算法基础

来源&#xff1a; bilibili数据结构与算法基础&#xff08;青岛大学-王卓&#xff09; 1.2 基本概念和术语 数据结构 数据元素之间的关系称为结构&#xff0c;数据结构是相互之间存在一种或多种特定关系的数据元素的集合。 数据结构的两个层次&#xff1a;逻辑结构和物理结构…

数据结构与算法基础(一)

大家好 我是makasa 这个栏目呢&#xff0c;我会按照我之前通过视频学习的一个笔记来记录. 同时,通过写这几篇blog来巩固一下我的基础 数据结构与算法&#xff0c;顾名思义&#xff0c;分为两个部分&#xff1a;数据结构、算法。那它们各自具体概述是什么呢。让我们看以下两个图…

【算法】算法基础

文章目录 1. 字符串1.1LeetCode151 Reverse Words in a String1.2LeetCode557 Reverse Words in a String III1.3统计字符串字母&#xff0c;数字&#xff0c;空格和其他字符个数1.4把字符串转换成整数1.5回文字符串 2.整数2.1LeetCode7 Reverse Integer2.2判断一个数是不是质数…

算法基础---基础算法

文章目录 快速排序归并排序二分 整数二分浮点数二分高精度 高精度加法高精度减法高精度乘法高精度除法前缀和 一维前缀和二维前缀和差分 一维差分二维差分双指针位运算离散化区间合并 一、快速排序 思想&#xff1a;1.首先确定一个分界点&#xff08;随机取任意一点为…

算法基础知识总结(基础算法)

算法基础知识总结 Efficient procedures for solving problems on large inputs. 一、基础算法 1、快速排序 1、类别&#xff1a;快速排序是一种 交换排序&#xff0c;冒泡排序也是一种交换排序。 2、原理&#xff1a;将未排序的元素根据一个作为基准的主元&#xff08;Pi…

算法基础知识

一、算法的定义 算法&#xff1a;对特定问题求解步骤的一种描述&#xff0c;是为解决一个或一类问题给出的一个确定的、有限长的操作序列。 二、算法与程序的区别与联系 区别&#xff1a; 程序&#xff1a;与某种编程语言有关&#xff0c;能直接在机器上运行。 算法&#xff1a…

算法基础简介

一、什么是算法 在数学领域&#xff0c;算法是为了解决某一类问题的公式和思想。 在计算机领域&#xff0c;本质是一些计算机指令&#xff0c;解决特定运算和逻辑问题。 算法的掌握程度&#xff0c;一方面可以检验程序员对计算机底层的了解&#xff0c;一方面也…

Unity 移动的几种方法(从某一点移动到另外一点)

对于unity的几种移动方法,在以下给出整理 1 方向*速度 2 vector.lerp 与目标点永远不会重合 3 vector.MoveTowards 会与目标点重合 4 translate方法 纯移动 5 WASD键盘方法 刚 体方法 7.鼠标方法 捕鱼达人 8.射线方法(指哪到哪) Controll…

Unity 控制物体移动

目录 1、通过改变物体的位置使物体移动 2、通过给物体施加力使物体移动 3、移动characterController以及碰撞检测 一、相关代码展示 1、通过改变物体的位置使物体移动 using System.Collections; using System.Collections.Generic; using UnityEngine;public class move …

unity3D人物移动的实现(一)

人物的移动&#xff0c;首先要考虑人物与地面的碰撞&#xff0c;碰撞发生的条件是&#xff0c;两者必须都为碰撞体&#xff0c;且至少有一方为刚体&#xff0c;为了方便&#xff0c;我们就给人物加上刚体属性和碰撞体。 1首先是碰撞体属性&#xff0c;人形使用胶囊碰撞体&#…

Unity让物体跟随鼠标移动

前言 最近在学习Unity&#xff0c;记录下学习的成果吧。本文最终结果是要实现一个小飞机跟随鼠标移动的效果。看下图片。 向量 在Unity中&#xff0c;每个对象都有自己的位置属性&#xff0c;组件叫做Transform,通过Transform可以获取对象的位置属性。在上面的实例中&#…

01_Unity常用的移动方法

文章目录 基础框架匀速移动变速移动自定义变速运动总结&#xff1a; 在制作一款游戏的时候&#xff0c;经常需要对物体的位置进行移动&#xff0c;我们希望这个移动是具有多样性的&#xff0c;并且可操作的。 C#中提供了非常丰富的移动代码工具&#xff0c;通过这些工具我们可以…

Unity点击物体后,移动到物体所在位置

Unity点击物体后&#xff0c;移动到物体所在位置 方法一&#xff1a;OnMouse检测&#xff08;需要Collider组件&#xff09; 脚本挂在被点击的物体上 using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement;/// <summary> /// 缺点…

Unity2D教程:人物移动

关注专栏&#xff0c;持续更新哦 教程总目录 按键 自带的Input有GetAxisRaw来获取按下按键后所对应的值&#xff0c;Input.GetAxisRaw(“Horizontal”)在按下D或右箭头返回1&#xff0c;A或左箭头返回-1&#xff1b;Input.GetAxisRaw(“Vertical”)同理。 Input.GetAxis会根…

Unity物体跟随鼠标移动

刚开始在将鼠标点转换为世界坐标时&#xff0c;我以为可以直接使用Unity的Camera.main.ScreenToWorldPoint( Input.mousePosition ) 就完事了&#xff0c;事实证明我想的太简单了。在我们使用这个API将鼠标屏幕点&#xff08;Screen&#xff09;转换成世界坐标&#xff08;Worl…

Unity中物体移动方法详解

一&#xff1a;Transform ——transform.Translate&#xff1a;不会考虑到碰撞transform.Translate(Vector3 targetPos&#xff0c;指定参照坐标系(默认为Space.Self)) ——transform.position&#xff1a;直接改变物体的坐标 二&#xff1a;刚体 ——MovePosition ——veloc…

【Unity】如何优雅地移动物体-8个方法

在游戏开发中&#xff0c;如何移动物体&#xff1f;是我们需要思考的事情。 Unity 引擎也提供了众多的方法&#xff0c;每个开发者的使用习惯也各不相同&#xff0c;所以往往不是很清楚在这种场景下哪种方式最好的或者最有效的。 那么&#xff0c;这篇文章&#xff0c;我想分享…