数据结构与算法入门
1.稀疏数组
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jt0Dj8zT-1659147662032)(C:\Users\mashiro\AppData\Roaming\Typora\typora-user-images\image-20220725220852410.png)]
2.队列(排队)
1.队列是一个有序列表,可以用数组或是链表来实现。
2.遵循先入先出的原则。即先存入队列的数据,要先取出。后存入的要后取出
3.队列的示意图[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zh8aIAgt-1659147662033)(C:\Users\mashiro\AppData\Roaming\Typora\typora-user-images\image-20220726150258306.png)]
4.数组模拟队列
1.将数据存入队列时称为“addQueue”,addQueue的处理需要有两个步骤:思路分析
1.将尾指针往后移:rear + 1,当front==rear【空】
2.若尾指针rear小于队列的最大下标maxSize - 1,则将数据存入rear所指的数组元素中,否则无法存入数据。rear == maxSize - 1【队列满】
public class as {
public static void main(String[] args) throws IOException {
ArrayQueue queue = new ArrayQueue(3);
char key;
Scanner scanner = new Scanner(System.in);
boolean loop = true;
while(loop)
{
System.out.println("s(show): 显示队列");
System.out.println("e(exit): 退出程序");
System.out.println("a(add): 添加数据到队列");
System.out.println("g(get): 从队列取出数据");
System.out.println("h(head): 查看队列头的数据");
key = scanner.next().charAt(0);//接收一个字符
switch (key) {
case 's':
queue.showQueue();
break;
case 'a':
System.out.println("输出一个数");
int value = scanner.nextInt();
queue.addQueue(value);
break;
case 'g': //取出数据
try {
int res = queue.getQueue();
System.out.printf("取出的数据是%d\n", res);
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
break;
case 'h': //查看队列头的数据
try {
int res = queue.headQueue();
System.out.printf("队列头的数据是%d\n", res);
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
break;
case 'e': //退出
scanner.close();
loop = false;
break;
default:
break;
}
System.out.println("程序退出");
}
}
// 使用数组模拟队列-编写一个ArrayQueue类
}
class ArrayQueue {
private int maxSize; // 表示数组的最大容量
private int front; // 队列头
private int rear; // 队列尾
private int[] arr; // 该数据用于存放数据, 模拟队列
// 创建队列的构造器
public ArrayQueue(int arrMaxSize) {
maxSize = arrMaxSize;
arr = new int[maxSize];
front = -1; // 指向队列头部,分析出front是指向队列头的前一个位置.
rear = -1; // 指向队列尾,指向队列尾的数据(即就是队列最后一个数据)
}
// 判断队列是否满
public boolean isFull() {
return rear == maxSize - 1;
}
// 判断队列是否为空
public boolean isEmpty() {
return rear == front;
}
// 添加数据到队列
public void addQueue(int n) {
// 判断队列是否满
if (isFull()) {
System.out.println("队列满,不能加入数据~");
return;
}
rear++; // 让rear 后移
arr[rear] = n;
}
// 获取队列的数据, 出队列
public int getQueue() {
// 判断队列是否空
if (isEmpty()) {
// 通过抛出异常
throw new RuntimeException("队列空,不能取数据");
}
front++; // front后移
return arr[front];
}
// 显示队列的所有数据
public void showQueue() {
// 遍历
if (isEmpty()) {
System.out.println("队列空的,没有数据~~");
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.printf("arr[%d]=%d\n", i, arr[i]);
}
}
// 显示队列的头数据, 注意不是取出数据
public int headQueue() {
// 判断
if (isEmpty()) {
throw new RuntimeException("队列空的,没有数据~~");
}
return arr[front + 1];
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6wb5lWgl-1659147662034)(C:\Users\mashiro\AppData\Roaming\Typora\typora-user-images\image-20220726165636320.png)]
2.栈
1.使用数组来模拟栈
2.定义一个top来表示栈,初始化为-1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LmGUxGIO-1659147662034)(C:\Users\mashiro\AppData\Roaming\Typora\typora-user-images\image-20220727093815336.png)]
基本算法(1.冒泡 + 选择 + 插入) 2 .(希尔 + 归并 + 快排) 3.(堆 + 基数 + 桶)
1.冒泡
2.选择
3.插入排序
public void insert(int arr[]){
int temp;
int index;
for (int i = 1; i < arr.length; i++) {//自己写的用下表索引去
temp = arr[i];
index = i;
for (int j = i; j >= 0; j--) {
if (arr[j] > temp){//****
arr[index] = arr[j];
arr[j] = temp;
index = j;
}
}
}
}
4.希尔排序(分组 + 插入排序)
public void shellSort(int arr[]) {
int temp;
int index;
int group = arr.length;
for (int gap = group / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {//这里是核心代码,
//因为从i = gap开始的时候就是从分组的第二个开始计算, 当前面排之后
//前面都是从小到大排的,所以后面只需要再判定一次就可以了
temp = arr[i];
index = i;
if (arr[i - gap] > temp){
while (index - gap >= 0 && temp < arr[index - gap]){
arr[index] = arr[index - gap];
arr[index - gap] = temp;
index = index - gap;
}
}
}
}
}
}
5.快速排序(二分 + 冒泡优化)
int l = left; //左下标
int r = right; //右下标
//pivot 中轴值
int pivot = arr[(left + right) / 2];
int temp = 0; //临时变量,作为交换时使用
//while循环的目的是让比pivot 值小放到左边
//比pivot 值大放到右边
while( l < r) {
//在pivot的左边一直找,找到大于等于pivot值,才退出
while( arr[l] < pivot) {
l += 1;
}
//在pivot的右边一直找,找到小于等于pivot值,才退出
while(arr[r] > pivot) {
r -= 1;
}
//如果l >= r说明pivot 的左右两的值,已经按照左边全部是
//小于等于pivot值,右边全部是大于等于pivot值
//交换
if (l < r) {
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
if(arr[l] == pivot) {
r -= 1;
}
if(arr[r] == pivot) {
l += 1;
}
}
//如果交换完后,发现这个arr[l] == pivot值 相等 r--, 前移
//如果交换完后,发现这个arr[r] == pivot值 相等 l++, 后移
if (l == r) {
l += 1;
r -= 1;
}
}
// 如果 l == r, 必须l++, r--, 否则为出现栈溢出
//向左递归
if(left < r) {
quickSort(arr, left, r);
}
//向右递归
if(right > l) {
quickSort(arr, l, right);
}
两个if判断是必须要的,因为当序列中存在相同值的时候,终究会走到两个相同值相邻那一步,此时内层的两个while循环进不去,也就没办法移位,会陷入无限循环,if判断起的作用就是移位得以跳出外层while
6.归并算法(分治算法)
public static void mergeSort(int arr[], int left, int right, int temp[]) {
if (left < right) {
int mid = (left + right) / 2;
mergeSort(arr, left, mid, temp);
mergeSort(arr, mid + 1, right, temp);
merge(arr, left, mid, right, temp);
}
}
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int l = left;
int r = mid + 1;
int t = 0;//temp 的首下标
while (l <= mid && r <= right) {
if (arr[l] <= arr[r]) {
temp[t] = arr[l];
l++;
t++;
} else {
temp[t] = arr[r];
t++;
r++;
}
}
while (l <= mid){
temp[t] = arr[l];
t++;
l++;
}
while (r <= right){
temp[t] = arr[r];
t++;
r++;
}
int L = left;
t = 0;
while (L <= right){
arr[L] = temp[t];
L++;
t++;
}//挺简单不要注释
}
7.基数排序
//1. 得到数组中最大的数的位数
int max = arr[0]; //假设第一数就是最大数
for(int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
//得到最大数是几位数
int maxLength = (max + "").length();
//定义一个二维数组,表示10个桶, 每个桶就是一个一维数组
//说明
//1. 二维数组包含10个一维数组
//2. 为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.length
//3. 名明确,基数排序是使用空间换时间的经典算法
int[][] bucket = new int[10][arr.length];
//为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数
//可以这里理解
//比如:bucketElementCounts[0] , 记录的就是 bucket[0] 桶的放入数据个数
int[] bucketElementCounts = new int[10];
//这里我们使用循环将代码处理
for(int i = 0 , n = 1; i < maxLength; i++, n *= 10) {
//(针对每个元素的对应位进行排序处理), 第一次是个位,第二次是十位,第三次是百位..
for(int j = 0; j < arr.length; j++) {
//取出每个元素的对应位的值
int digitOfElement = arr[j] / n % 10;
//放入到对应的桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++;
}
//按照这个桶的顺序(一维数组的下标依次取出数据,放入原来数组)
int index = 0;
//遍历每一桶,并将桶中是数据,放入到原数组
for(int k = 0; k < bucketElementCounts.length; k++) {
//如果桶中,有数据,我们才放入到原数组
if(bucketElementCounts[k] != 0) {
//循环该桶即第k个桶(即第k个一维数组), 放入
for(int l = 0; l < bucketElementCounts[k]; l++) {
//取出元素放入到arr
arr[index++] = bucket[k][l];
}
}
//第i+1轮处理后,需要将每个 bucketElementCounts[k] = 0 !!!!
bucketElementCounts[k] = 0;
}
for(int k = 0; k < bucketElementCounts.length; k++) {
//如果桶中,有数据,我们才放入到原数组
if(bucketElementCounts[k] != 0) {
//循环该桶即第k个桶(即第k个一维数组), 放入
for(int l = 0; l < bucketElementCounts[k]; l++) {
//取出元素放入到arr
arr[index++] = bucket[k][l];
}
}
//第i+1轮处理后,需要将每个 bucketElementCounts[k] = 0 !!!!
bucketElementCounts[k] = 0;
}