算法拾遗二十二之Dijstra算法优化+认识一些经典的递归过程

news2024/11/25 10:53:25

Dijstra算法优化

	public static class NodeRecord {
		public Node node;
		public int distance;

		public NodeRecord(Node node, int distance) {
			this.node = node;
			this.distance = distance;
		}
	}

	public static class NodeHeap {
		private Node[] nodes; // 实际的堆结构
		// key 某一个node, value为上面堆中的位置
		private HashMap<Node, Integer> heapIndexMap;
		// key 某一个节点, value 从源节点出发到该节点的目前最小距离
		private HashMap<Node, Integer> distanceMap;
		private int size; // 堆上有多少个点

		public NodeHeap(int size) {
			nodes = new Node[size];
			heapIndexMap = new HashMap<>();
			distanceMap = new HashMap<>();
			size = 0;
		}

		public boolean isEmpty() {
			return size == 0;
		}

		// 有一个点叫node,现在发现了一个从源节点出发到达node的距离为distance
		// 判断要不要更新,如果需要的话,就更新
		public void addOrUpdateOrIgnore(Node node, int distance) {
		   //判断是否在堆上,如果在堆上则看是否更新
			if (inHeap(node)) {
				distanceMap.put(node, Math.min(distanceMap.get(node), distance));
				//在堆上向上调整,因为只可能不变或者变小
				insertHeapify(node, heapIndexMap.get(node));
			}
			//如果没有进来过则新增
			if (!isEntered(node)) {
				//先将点放到堆的最后
				nodes[size] = node;
				//表中加node给node下标和距离,看能否调整
				heapIndexMap.put(node, size);
				distanceMap.put(node, distance);
				insertHeapify(node, size++);
			}
		}

		public NodeRecord pop() {
			//堆顶元素
			NodeRecord nodeRecord = new NodeRecord(nodes[0], distanceMap.get(nodes[0]));
			//拿最后一个节点和0位置做交换
			swap(0, size - 1);
			//最后一个节点的下标改为-1代表已经从堆上弹出了
			heapIndexMap.put(nodes[size - 1], -1);
			//距离表删除了
			distanceMap.remove(nodes[size - 1]);
			// free C++同学还要把原本堆顶节点析构,对java同学不必
			//堆上将最后位置给清除
			nodes[size - 1] = null;
			heapify(0, --size);
			return nodeRecord;
		}

		private void insertHeapify(Node node, int index) {
			while (distanceMap.get(nodes[index]) < distanceMap.get(nodes[(index - 1) / 2])) {
				swap(index, (index - 1) / 2);
				index = (index - 1) / 2;
			}
		}

		private void heapify(int index, int size) {
			int left = index * 2 + 1;
			while (left < size) {
				int smallest = left + 1 < size && distanceMap.get(nodes[left + 1]) < distanceMap.get(nodes[left])
						? left + 1
						: left;
				smallest = distanceMap.get(nodes[smallest]) < distanceMap.get(nodes[index]) ? smallest : index;
				if (smallest == index) {
					break;
				}
				swap(smallest, index);
				index = smallest;
				left = index * 2 + 1;
			}
		}

		/**
		 * 一个节点是否进入过堆里面
		 * @param node
		 * @return
		 */
		private boolean isEntered(Node node) {
			return heapIndexMap.containsKey(node);
		}

		/**
		 * 这个节点是否在堆上 如果点从堆上弹出则标记为-1
		 * @param node
		 * @return
		 */
		private boolean inHeap(Node node) {
			return isEntered(node) && heapIndexMap.get(node) != -1;
		}

		private void swap(int index1, int index2) {
			heapIndexMap.put(nodes[index1], index2);
			heapIndexMap.put(nodes[index2], index1);
			Node tmp = nodes[index1];
			nodes[index1] = nodes[index2];
			nodes[index2] = tmp;
		}
	}

	// 改进后的dijkstra算法
	// 从head出发,所有head能到达的节点,生成到达每个节点的最小路径记录并返回,size代表图里面有多少个节点
	public static HashMap<Node, Integer> dijkstra2(Node head, int size) {
		NodeHeap nodeHeap = new NodeHeap(size);
		nodeHeap.addOrUpdateOrIgnore(head, 0);
		HashMap<Node, Integer> result = new HashMap<>();
		while (!nodeHeap.isEmpty()) {
			//弹出堆顶元素
			NodeRecord record = nodeHeap.pop();
			//获取节点
			Node cur = record.node;
			//获取起始点到当前点的距离
			int distance = record.distance;
			for (Edge edge : cur.edges) {
				//当前边发散出去的权重加上起始点到当前点的距离
				nodeHeap.addOrUpdateOrIgnore(edge.to, edge.weight + distance);
			}
			result.put(cur, distance);
		}
		return result;
	}

暴力递归

汉诺塔问题


	public static void hanoi1(int n) {
		leftToRight(n);
	}

	// 请把1~N层圆盘 从左 -> 右【除basecase外需要先从左移动到中间,再从中间移动到右边】
	public static void leftToRight(int n) {
		//将最下面的圆盘从左直接移到右
		if (n == 1) { // base case
			System.out.println("Move 1 from left to right");
			return;
		}
		//剩余的n-1个圆盘从左移动到中
		leftToMid(n - 1);
		System.out.println("Move " + n + " from left to right");
		//将剩余的圆盘从中移动到右
		midToRight(n - 1);
	}

	// 请把1~N层圆盘 从左 -> 中【除basecase外需要先从左移动到右边再从右边移动到中间】
	public static void leftToMid(int n) {
		if (n == 1) {
			System.out.println("Move 1 from left to mid");
			return;
		}
		//将n-1个圆盘从左边移动到右边去
		leftToRight(n - 1);
		System.out.println("Move " + n + " from left to mid");
		//再将n-1个圆盘从右边移动到中间
		rightToMid(n - 1);
	}
//除basecase外需要先从右边先移动到左边,再从左边移动到右边
	public static void rightToMid(int n) {
		if (n == 1) {
			System.out.println("Move 1 from right to mid");
			return;
		}
		rightToLeft(n - 1);
		System.out.println("Move " + n + " from right to mid");
		leftToMid(n - 1);
	}
//除basecase外需要先将中间的移动到左边,再将左边的移动到右边
	public static void midToRight(int n) {
		if (n == 1) {
			System.out.println("Move 1 from mid to right");
			return;
		}
		midToLeft(n - 1);
		System.out.println("Move " + n + " from mid to right");
		leftToRight(n - 1);
	}
//除basecase外需要先将中间的移动到右边,再将右边的移动到左边
	public static void midToLeft(int n) {
		if (n == 1) {
			System.out.println("Move 1 from mid to left");
			return;
		}
		midToRight(n - 1);
		System.out.println("Move " + n + " from mid to left");
		rightToLeft(n - 1);
	}
	//除basecase外需要先将右边的移动到中间,再将中间的移动到左边
	public static void rightToLeft(int n) {
		if (n == 1) {
			System.out.println("Move 1 from right to left");
			return;
		}
		rightToMid(n - 1);
		System.out.println("Move " + n + " from right to left");
		midToLeft(n - 1);
	}

在这里插入图片描述
方法2:
定义from,to,other,这里的from,to和other都可能是左中右的任意一个,然后假设从from移动到to上,则需要经历以下三个流程
1)将1-n-1的圆盘从from移动到other,to成为了other
2)将剩余的一个n圆盘从from移动到to
3)将剩余的点从other移动到to区里面,这时的from作为上一步的other

	public static void hanoi2(int n) {
		if (n > 0) {
			func(n, "left", "right", "mid");
		}
	}
	//1-N 在:from
	//去:to
	//另一个:other
	public static void func(int N, String from, String to, String other) {
		if (N == 1) { // base
			System.out.println("Move 1 from " + from + " to " + to);
		} else {
			//第一步将1~N-1的圆盘从from移动到other
			func(N - 1, from, other, to);
			//第二步是n圆盘自己直接挪动
			System.out.println("Move " + N + " from " + from + " to " + to);
			//第三步将圆盘从other移动到to
			func(N - 1, other, to, from);
		}
	}

打印一个字符串的全部子序列

在这里插入图片描述
如上图为例,要找1,2,3这个序列的所有子序列,则可以规划为如上图的形式,关于每个数字是否需要来完成所有情况的获取

// s -> "abc"
	public static List<String> subs(String s) {
		char[] str = s.toCharArray();
		String path = "";
		List<String> ans = new ArrayList<>();
		process1(str, 0, ans, path);
		return ans;
	}

	//规定递归的含义
	// str 固定参数不变
	// 来到了str[index]字符,index是位置
	// str[0..index-1]已经走过了!之前的决定,都在path上
	// 之前的决定已经不能改变了,就是path
	// str[index....]还能决定,之前已经确定,而后面还能自由选择的话,
	// 把所有生成的子序列,放入到ans里去
	public static void process1(char[] str, int index, List<String> ans, String path) {
		//index到终止位置了则直接结束
		if (index == str.length) {
			ans.add(path);
			return;
		}
		// 没有要index位置的字符(path没变)
		process1(str, index + 1, ans, path);
		// 要了index位置的字符(path变化了)
		process1(str, index + 1, ans, path + str[index]);
	}

打印一个字符串的全部子序列,不出现重复

public static List<String> subsNoRepeat(String s) {
		char[] str = s.toCharArray();
		String path = "";
		HashSet<String> set = new HashSet<>();
		process2(str, 0, set, path);
		List<String> ans = new ArrayList<>();
		for (String cur : set) {
			ans.add(cur);
		}
		return ans;
	}

	public static void process2(char[] str, int index, HashSet<String> set, String path) {
		if (index == str.length) {
			set.add(path);
			return;
		}
		String no = path;
		process2(str, index + 1, set, no);
		String yes = path + str[index];
		process2(str, index + 1, set, yes);
	}

打印一个字符串的全排列

全排列就是所有的字符都得要,只是说顺序可以不一样。
a,b,c,d 分别代表0-3位置,0位置是四个字符中选一个,
1位置是3种字符选一个,2位置是2种字符选一个,3位置只剩下一个字符。
在这里插入图片描述
绘制成如上图,将所有路都摊开。

暴力解法:

 public static List<String> permutation1(String s) {
        List<String> ans = new ArrayList<>();
        if (s == null || s.length() == 0) {
            return ans;
        }
        char[] str = s.toCharArray();
        ArrayList<Character> rest = new ArrayList<Character>();
        for (char cha : str) {
            rest.add(cha);
        }
        String path = "";
        f(rest, path, ans);
        return ans;
    }
    
    //剩下的字符全在rest里面
    //答案在ans里面
    public static void f(ArrayList<Character> rest, String path, List<String> ans) {
        //之前的所有决定都做完了
        if (rest.isEmpty()) {
            ans.add(path);
        } else {
            int N = rest.size();
            for (int i = 0; i < N; i++) {
                char cur = rest.get(i);
                rest.remove(i);
                f(rest, path + cur, ans);
                //恢复现场
                rest.add(i, cur);
            }
        }
    }

如上方法不够好,下面再演示另一个版本的方法:
在这里插入图片描述
依次做交换,

 public static List<String> permutation2(String s) {
        List<String> ans = new ArrayList<>();
        if (s == null || s.length() == 0) {
            return ans;
        }
        char[] str = s.toCharArray();
        g1(str, 0, ans);
        return ans;
    }

    //所有的结果在str里面
    public static void g1(char[] str, int index, List<String> ans) {
        if (index == str.length) {
            ans.add(String.valueOf(str));
        } else {
            //如果有的换
            for (int i = index; i < str.length; i++) {
                //index的值跟i换,然后跑所有的支路,最后再换回来。
                swap(str, index, i);
                g1(str, index + 1, ans);
                //下一个支路到来前恢复现场
                swap(str, index, i);
            }
        }
    }
    public static void swap(char[] chs, int i, int j) {
        char tmp = chs[i];
        chs[i] = chs[j];
        chs[j] = tmp;
    }

打印字符串的全排列并去重

根据ascii码作为一个boolean数组来确定是否需要交换位置
从而进行去重

    public static List<String> permutation3(String s) {
        List<String> ans = new ArrayList<>();
        if (s == null || s.length() == 0) {
            return ans;
        }
        char[] str = s.toCharArray();
        g2(str, 0, ans);
        return ans;
    }

    public static void g2(char[] str, int index, List<String> ans) {
        if (index == str.length) {
            ans.add(String.valueOf(str));
        } else {
            boolean[] visited = new boolean[256];
            for (int i = index; i < str.length; i++) {
            //这个字符是否试过的,试过的则不管
                if (!visited[str[i]]) {
                    visited[str[i]] = true;
                    swap(str, index, i);
                    g2(str, index + 1, ans);
                    swap(str, index, i);
                }
            }
        }
    }

    public static void swap(char[] chs, int i, int j) {
        char tmp = chs[i];
        chs[i] = chs[j];
        chs[j] = tmp;
    }

给你一个栈,请你逆序这个栈,不能申请额外的数据结构,只能使用递归函数

public static void reverse(Stack<Integer> stack) {
		if (stack.isEmpty()) {
			return;
		}
		int i = f(stack);
		reverse(stack);
		stack.push(i);
	}

	// 栈底元素移除掉
	// 上面的元素盖下来
	// 返回移除掉的栈底元素
	public static int f(Stack<Integer> stack) {
		int result = stack.pop();
		if (stack.isEmpty()) {
			return result;
		} else {
			int last = f(stack);
			stack.push(result);
			return last;
		}
	}

	public static void main(String[] args) {
		Stack<Integer> test = new Stack<Integer>();
		test.push(1);
		test.push(2);
		test.push(3);
		test.push(4);
		test.push(5);
		reverse(test);
		while (!test.isEmpty()) {
			System.out.println(test.pop());
		}

	}

f函数流程
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
将2压栈回去,返回我接住的last到f1
在这里插入图片描述
在这里插入图片描述
最后返回3:
在这里插入图片描述
reverse函数流程:
在这里插入图片描述

import java.util.Stack;

// 栈只提供push、pop、isEmpty三个方法
// 请完成无序栈的排序,要求排完序之后,从栈顶到栈底从小到大
// 只能使用栈提供的push、pop、isEmpty三个方法、以及递归函数
// 除此之外不能使用任何的容器,任何容器都不许,连数组也不行
// 也不能自己定义任何结构体
// 就是只用:
// 1) 栈提供的push、pop、isEmpty三个方法
// 2) 简单返回值的递归函数
public class Code01_SortStackUsingRecursive {

	public static void sort(Stack<Integer> stack) {
		if (stack.isEmpty()) {
			return;
		}
		int deep = size(stack);
		while (deep > 0) {
			int max = findMax(stack, deep);
			int k = findMaxTimes(stack, deep, max);
			maxDown(stack, deep, max, k);
			deep -= k;
		}
	}

	// 求栈的大小
	// 但是不改变栈的任何数据状况
	public static int size(Stack<Integer> stack) {
		if (stack.isEmpty()) {
			return 0;
		}
		int hold = stack.pop();
		int size = size(stack) + 1;
		stack.push(hold);
		return size;
	}

	// 从stack顶部出发,只往下找deep层
	// 返回最大值
	// 完全不改变stack的任何数据状况
	public static int findMax(Stack<Integer> stack, int deep) {
		if (deep == 0) {
			return Integer.MIN_VALUE;
		}
		int num = stack.pop();
		int restMax = findMax(stack, deep - 1);
		int ans = Math.max(num, restMax);
		stack.push(num);
		return ans;
	}

	// 已知从stack顶部出发,只往下找deep层,最大值是max
	// 返回这个最大值出现了几次,只找到deep层!再往下不找了!
	// 完全不改变stack的任何数据状况
	public static int findMaxTimes(Stack<Integer> stack, int deep, int max) {
		if (deep == 0) {
			return 0;
		}
		int num = stack.pop();
		int times = findMaxTimes(stack, deep - 1, max);
		times += num == max ? 1 : 0;
		stack.push(num);
		return times;
	}

	// 已知从stack顶部出发,只往下找deep层,最大值是max
	// 并且这个max出现了k次
	// 请把这k个max沉底,不是沉到stack整体的底部,而是到deep层
	// stack改变数据状况,但是只在从顶部到deep层的范围上改变
	public static void maxDown(Stack<Integer> stack, int deep, int max, int k) {
		if (deep == 0) {
			for (int i = 0; i < k; i++) {
				stack.push(max);
			}
		} else {
			int cur = stack.pop();
			maxDown(stack, deep - 1, max, k);
			if (cur < max) {
				stack.push(cur);
			}
		}
	}

	// 为了测试
	// 生成随机栈
	public static Stack<Integer> generateRandomStack(int n, int v) {
		Stack<Integer> ans = new Stack<Integer>();
		for (int i = 0; i < n; i++) {
			ans.add((int) (Math.random() * v));
		}
		return ans;
	}

	// 为了测试
	// 检测栈是不是有序的
	public static boolean isSorted(Stack<Integer> stack) {
		int step = Integer.MIN_VALUE;
		while (!stack.isEmpty()) {
			if (step > stack.peek()) {
				return false;
			}
			step = stack.pop();
		}
		return true;
	}

	// 为了测试
	public static void main(String[] args) {
		Stack<Integer> test = new Stack<Integer>();
		test.add(1);
		test.add(5);
		test.add(4);
		test.add(5);
		test.add(3);
		test.add(2);
		test.add(3);
		test.add(1);
		test.add(4);
		test.add(2);
		// 1 5 4 5 3 2 3 1 4 2
		sort(test);
		while (!test.isEmpty()) {
			System.out.println(test.pop());
		}

		int n = 10;
		int v = 20;
		int testTimes = 2000;
		System.out.println("测试开始");
		for (int i = 0; i < testTimes; i++) {
			Stack<Integer> stack = generateRandomStack(n, v);
			sort(stack);
			if (!isSorted(stack)) {
				System.out.println("出错了!");
				break;
			}
		}
		System.out.println("测试结束");
	}

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/86617.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VS2019封装C++接口至可执行动态链接库.dll

VS2019封装C接口至可执行动态链接库.dll 本文主要总结如何对已实现的C函数、类进行封装&#xff0c;以便在其他平台或者语言上进行便捷使用或者二次开发&#xff01; 重点参考&#xff1a; 1.https://blog.csdn.net/hai_fellow_Z/article/details/117290517 2.https://blog.csd…

打工人的发展困境

近期在公司内部QE社区举办了一场研讨会&#xff0c;主题是《QA角色职业发展机遇讨论》。收集到以下观点&#xff0c;觉得挺有意思&#xff0c;又不局限于特定角色&#xff0c;于是用XX代替QA。 无论是在公司内部还是外部&#xff0c;XX都是单独作战居多&#xff0c;从他人身上…

都在说00后是躺平的一代,公司刚来的00后却把我卷哭了

上个月公司来了许多新面孔&#xff0c;其中居然有一个是00年的&#xff0c;虽然也给公司带来了一些新的血液&#xff0c;但也让我意识到&#xff0c;自己年级确实不小了。这小老弟是去年毕业的&#xff0c;在某软件公司干了半年&#xff0c;现在跳槽到我们公司起薪18K&#xff…

通过数据分析提升客流量和销售额

只有把每一位来光临的顾客都当作最后一位顾客&#xff0c;我们才可能真正实现营销和服务管理的精细化&#xff0c;正如只有把自己的每一天都当作生命的最后一天来对待的人才可能真正珍惜时间和生命一样。 如何提高门店的销售额可以说是大部分店长都十分困惑的问题&#xff0c;…

12月13日 OpenCV 实战基础学习笔记——Harris、SIFT

文章目录前言一、Harris 角点检测1、公式推导2、找到 E(u,v)E(u, v)E(u,v) 的最大值二、SIFT1、关键点定位 keypoint localisation2、特征描述 feature description前言 本文为12月13日 OpenCV 实战基础学习笔记&#xff0c;分为两个章节&#xff1a; Harri 角点检测&#xf…

docker安装Gitlab、修改密码、创建组、创建项目、创建用户、Git拉取代码

安装 建议内存大于2G 开放防火墙端口80和配置映射文件夹 firewall-cmd --zonepublic --add-port80/tcp --permanent firewall-cmd --reload mkdir -p /docker_data/gitlab/{data,logs,config}启动Gitlab容器&#xff08;启动容器之前确保22&#xff0c;80&#xff0c;443端口没…

图解外包验收流程V2.0及详细说明实例

详细说明 01、【开发计划内容】-标准模板{文档} 1.包含二级模块开发计划&#xff0c;标注里程碑验收节点&#xff1b; 2.包含架构设计、数据库设计输出时间&#xff1b; 3.包含接口开发计划及其接口明细&#xff1b; 4.所有节点分配到具体的人和具体的开始结束时间&#xff1…

Python读取Word文档中的Excel嵌入文件

今天群友提出一个问题&#xff1a; 给出Word示例如下&#xff1a; 对于这种嵌入文件在Word中都属于ole文件。 下面我们假设需要读取每个嵌入的Excel文件中的python工作表中的A1单元格。 python调用宏实现 首先我们看看如何调用com接口的宏代码实现这个效果&#xff0c;最终完…

SpringBoot多模块项目初始化搭建

&#x1f3b6; 文章简介&#xff1a;SpringBoot多模块项目初始化搭建 &#x1f4a1; 创作目的&#xff1a;详细介绍SpringBoot多模块项目的搭建 ☀️ 今日天气&#xff1a;阳光明媚 &#x1f4dd; 每日一言&#xff1a;不求事事圆满&#xff0c;但求事事甘心。 文章目录1、新建…

数据结构与算法——Java实现单链表、双向链表、环型链表、约瑟夫

目录 一、单链表 1.1 单链表基本介绍 1.2 分析与实现带head头结点的单向链表 1.2.1第一种方式&#xff1a; 尾部添加元素示意图 1.2.2 尾部添加元素的代码实现以及遍历链表的实现 1.2.3 第二种方式&#xff1a; 根据排名将节点插入到指定位置的示意图 1.2.4 根据排名将节…

MySQL入门到精通经典50题,看这一篇就够了

MySQL入门到精通经典50题学习笔记 pdf获取方式&#xff0c;公众号&#xff1a;每天都要努力coding回复&#xff1a;mysql经典50题 文章目录MySQL入门到精通经典50题学习笔记[toc]MySQL安装教程详解数据预览sql建表语句1.查询" 01 “课程比” 02 "课程成绩高的学生的…

基于鸽群算法优化的lssvm回归预测-附代码

基于鸽群算法优化的lssvm回归预测 - 附代码 文章目录基于鸽群算法优化的lssvm回归预测 - 附代码1.数据集2.lssvm模型3.基于鸽群算法优化的LSSVM4.测试结果5.Matlab代码摘要&#xff1a;为了提高最小二乘支持向量机&#xff08;lssvm&#xff09;的回归预测准确率&#xff0c;对…

你的项目需要自动化测试吗?看看这篇文章再说吧

什么是自动化测试&#xff1f; 通过代码的方式&#xff0c;实现测试用例的自动运行&#xff0c;评估运行结果&#xff0c;并对测试结果及异常情况进行记录。 为什么进行自动化测试&#xff1f; 纯手工测试会有好多重复性的操作&#xff0c;浪费时间&#xff0c;而且频繁的回…

央企招聘:中国大唐集团2023年度公开招聘公告

中国大唐集团科学技术研究总院有限公司&#xff08;以下简称科研总院&#xff09;是世界500强企业——中国大唐集团有限公司&#xff08;以下简称集团公司&#xff09;的全资子公司&#xff0c;是集团公司的科技创新中心、战略参谋本部、成果孵化中心、技术服务保障基地和科技人…

Qt+VS+VTK综合开发环境配置

说明 本文旨在介绍一种个人常用的开发环境&#xff0c;主要解决在VS中开发Qt项目的部分问题&#xff0c;以及解决使用基于Qt的VTK库开发时可能遇到的部分问题&#xff0c;并通过合理的配置提升新项目搭建时间 该教程使用版本为VS2017&#xff0c;Qt5.14.2以及VTK8.2&#xff…

git_No.1_版本管控的全流程

文章目录1.获取Git仓库1.1 已存在目录中初始化仓库1.2 克隆一个仓库2. 将变更提交到仓库2.1 查看当前文件状态2.2 跟踪新文件2.3 暂存已修改的文件2.4 忽略文件2.5 查看已暂存和未暂存的修改2.6 提交更新2.7 跳过使用暂存区2.8 移除文件3. 查看提交历史4.远程仓库4.1 查看远程仓…

HTTP介绍

目录 1.什么是HTTP&#xff1f; 2.HTTP的特点 3.HTTP的优点和缺点 4.HTTP请求数据格式 5.HTTP响应数据格式 6.GET请求和POST请求的区别 7.状态码分类说明 8.查看发送的请求数据包方式 1.什么是HTTP&#xff1f; Hyper Text Transfer Protocol,超文本传输协议&#xff…

[036]基于Vue+Nodejs的网上作业批改系统(含源码、数据库、万字课设报告)

文章目录一、项目介绍二、源码获取一、项目介绍 网络作业提交与批改系统&#xff1a;超强的作业批改项目&#xff0c;技术栈是VueNodeMysql&#xff0c;最重要的是有超详细的万字报告&#xff0c;一劳永逸&#xff0c;可冲~ 主要功能介绍&#xff1a; 1.管理员功能模块 管理员…

分享几个宝藏微信小程序

1.有数鸟&#xff1a;了解会员在各平台的消费明细 平时我们看视频听音乐&#xff0c;我们用的很多vip&#xff0c;你还记得你注册了哪些应用吗&#xff1f;有了这个小程序&#xff0c;就可以帮你记录每个平台的付款详情&#xff0c;以及总支出。 当我们添加会员项目时&#xff…

基于java+springboot+mybatis+vue+mysql的乒乓球预约管理系统

项目介绍 随着信息化时代的到来&#xff0c;管理系统都趋向于智能化、系统化&#xff0c;乒乓球馆预约管理系统 也不例外&#xff0c;但目前国内仍都使用人工管理&#xff0c;市场规模越来越大&#xff0c;同时信息量也越来越庞大&#xff0c;人工管理显然已无法应对时代的变…