文章目录
-
-
- 1. 数据结构与算法概要
- 2.数组
- 3.链表
- 4.数组的遍历与求和
- 5.链表的遍历与求和
- 6.链表的递归遍历 *
- 7.链表的逆置
- 8.栈和队列
- 9.用两个栈模拟队列
- 10.时间复杂度
- 11.空间复杂度
- 12.冒泡排序
- 13.选择排序
- 14.有序数组的合并
- 15.有序链表的合并
- 16.归并排序
- 17.简单快速排序
- 18.标准快速排序
- 19.大数相加
- 20.二维数据遍历
- 21.邻接链表的遍历
- 22.二维数组拓扑结构--图
- 23.树
- 24.二叉树的遍历
- 25.根据前序和中序还原二叉树
- 26.根据中序和后序还原二叉树
- 27.二叉树的深度优先搜索
- 28.二叉树的广度优先搜索
- 29.二叉树的比较
- 30.最小生成树
- 31.普利姆算法
- 32.克鲁斯卡尔算法
- 33.构建二叉排序
- 34. 二叉树单旋
- 35.二叉树双旋
- 36.树的深度优先搜索
- 37.数的广度优先搜索
- 38.图的深度优先搜索
- 39.图的广度优先搜索
- 40.哈夫曼树
- 41.斐波那契额数列
- 42.青蛙跳台阶
- 43.变态青蛙跳台阶
-
1. 数据结构与算法概要
数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术有关。
算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。
2.数组
特点:数组对象直接指向数组首地址, 物理空间上是连续的(线性排列),一维数据结构(线性数据j结构)。
优点:在查找的时候,效率高。(根据首地址+偏移量)
缺点:因为空间是连续的,所以当磁盘的空间碎片较多时,我们可能无法存入大数组。即使内存空间足够,也无法存储。
3.链表
特点:空间可以是不连续的。需要在存放数据的同时,多创建一个空间用来存放地址。
优点:不受空间大小的影响,只要有空间,就能存储数据,不会受空间连续的制约。
缺点:
(1),查找效率没有数组高。
(2),需要多开辟一块空间。
4.数组的遍历与求和
//求和方法
public static int sum(int[] arr){
if (arr == null) return 0;
int total = 0;
//遍历数组
for (int i = 0; i<arr.length; i++){
total += arr[i];
}
return total;
}
public static void main(String[] args) {
int[] arr = new int[]{
1,2,3,4,5,6,7,8};
System.out.println(sum(arr)); //输出:36
}
5.链表的遍历与求和
定义一个节点类
//定义节点
public class Node {
public int val;
public Node next;
public Node(int val){
this.val = val;}
}
遍历与求和
public class Test {
//遍历链表
public static void print(Node node) {
while (node != null) {
System.out.println(node.val);
node = node.next;
}
}
//链表求和
public static int sum(Node node) {
int total = 0;
while (node != null) {
total += node.val;
node = node.next;
}
return total;
}
public static void main(String[] args) {
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);
Node node4 = new Node(4);
node1.next = node2;
node2.next = node3;
node3.next = node4;
//遍历
print(node1); //输出:1,2,3,4
//求和
System.out.println(sum(node1)); //输出:10
}
}
6.链表的递归遍历 *
//递归 自己调自己
public static void print2(Node node){
if (node == null) return; //递归出口
System.out.println(node.val); //对每个节点的通用操作 //输出 1,2,3,4
print2(node.next); //向下递归点
System.out.println(node.val); //输出 4,3,2,1
}
public static void main(String[] args) {
Node node1 = new Node(1);
//... 节点创建同上
node3.next = node4;
//递归
print2(node1); //输出:1,2,3,4,4,3,2,1
}
7.链表的逆置
//在链表中,每一个节点都认为自己是根节点
//方法一
public static Node reverse(Node node){
if (node.next.next!=null){
Node result = reverse(node.next);
//让当前节点的下一个节点指向当前节点,比如当前节点node2.next.next= node3 ==> node3.next=node2
node.next.next = node;
//让当前节点指向null
node.next = null;
return result;
} else {
//此时 node.next.next == null ;此时当前节点是node3,下一个节点就是最后一个node4
Node result = node.next;
node.next.next = node;
node.next = null;
return result; //返回最后一个节点,也就是新的根节点node4
}
//方法二:去除冗余代码,简写
public static Node reverse2(Node node){
Node result = null;
if (node.next.next!=null) result = reverse(node.next);
else result = node.next;
node.next.next = node;
node.next = null;
return result;
}
测试:
public static void main(String[] args) {
Node node1 = new Node(1);
//...创建节点同上
node3.next = node4;
Node result = reverse(node1);
System.out.println(result);
Node result = reverse2(node1);
System.out.println(result);
}
}
结果: 打断点查看结果(两种逆置方法一样)
8.栈和队列
栈:
(1)栈是一种线性结构,栈中的元素遵循先入后出的原则,最先进入的元素所在位置叫做栈底,最后放入的元素所在位置叫做栈顶。
这种结构类似于盛放羽毛球的圆筒,一端封闭,另一端开口,先放入的羽毛球位于筒的底部(即栈底),后放入的羽毛球位于筒的入口(即栈顶)。
(2)栈也是一种抽象的逻辑结构,依赖于物理结构(如数组、链表)而存在。既可以使用数组实现,也可以使用链表实现。
(3)出栈、入栈的时间复杂都是O(1)。
用数组实现
//自定义栈
class MyStack{
//申请栈空间
Integer[] data;
//指向当前栈顶位置
Integer top = 0;
//初始化栈大小
public MyStack(int size){
this.data = new Integer[size];
}
//向栈中存入数据方法
public void push(Integer val) throws Exception{
//判断栈有没有空间
if (top==this.data.length){
throw new Exception("栈满了");
}
data[top] = val;
top++;
}
//从栈中取出数据
public Integer pop() throws Exception{
//判断栈是否为空
if (top==0){
throw new Exception("栈为空");
}
return data[--top];
}
测试 一 : 开辟大小为3的栈,添加三个数据
public static void main(String[] args) throws Exception {
MyStack myStack = new MyStack(3);
myStack.push(1);
myStack.push(2);
myStack.push(3);
System.out.println(myStack.pop());
System.out.println(myStack.pop());
System.out.println(myStack.pop());
}
输出结果:
测试二:添加四个数据
public static void main(String[] args) throws Exception {
MyStack myStack = new MyStack(3);
myStack.push(1);
myStack.push(2);
myStack.push(3);
myStack.push(4);
System.out.println(myStack.pop());
System.out.println(myStack.pop());
System.out.println(myStack.pop());
}
输出结果:
测试三: 当栈中没有数据
public static void main(String[] args) throws Exception {
MyStack myStack = new MyStack(3);
System.out.println(myStack.pop());
}
输出结果:
队列:
在队尾插入元素,在队首删除元素。
FIFO(先进先出),就向排队取票一样。
用链表实现
class MyQueue{
//指向列头
Node head = null;
//只想列底
Node tail = null;
//队列初始化大小
int maxSize;
//使用大小
int size;
public MyQueue(int maxSize){
this.maxSize = maxSize;
}
//存数据
public void push(int val) throws Exception{
if (size >= maxSize){
throw new Exception("队列满了");
}
Node node = new Node(val);
if (size==0){
head = node;
tail = node;
} else {
tail.next = node;
tail = node;
}
size++;
}
public int pop() throws Exception{
if (size == 0){
throw new Exception("空的");
}
int result = head.val;
head = head.next;
size--;
return result;
}
}
测试
public static void main(String[] args) throws Exception {
MyQueue myQueue = new MyQueue(3);
myQueue.push(1);
myQueue.push(2);
myQueue.push(3);
System.out.println(myQueue.pop());
System.out.println(myQueue.pop());
System.out.println(myQueue.pop());
}
输出:
9.用两个栈模拟队列
原理: 栈A存入数据,再取出数据,再将数据存入栈B,再从栈B中取出数据
class MyQueueWithStack{
MyStack stack1;
MyStack stack2;
public
MyQueueWithStack(int size) {
stack1 = new MyStack(size);
stack2 = new MyStack(size);
}
public void push(int val) throws Exception {
stack1.push(val);
}
public int pop() throws Exception {
while (!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
int result = stack2.pop();
return result;
}
测试:
public static void main(String[] args) throws Exception {
//用两个栈来模拟队列
MyQueueWithStack myQueueWithStack = new MyQueueWithStack(10);
myQueueWithStack.push(1);
myQueueWithStack.push(2);
myQueueWithStack.push(3);
System.out.println(myQueueWithStack.pop());
System.out.println(myQueueWithStack.pop());
System.out.println(myQueueWithStack.pop());
}
结果:
10.时间复杂度
假设我们有个数组,我们要查找数组中是否存在某个数。
每访问一个元素,时间复杂度记1。如果一个数组长度为n。如果我们用遍历的方式来进行操作,那么访问n次才能够确定。时间复杂度记为n。
常见时间复杂度:1,N,Logn,N^2
11.空间复杂度
用了多少额外的空间。
我们暂且估计原始数据的大小为n。
我们新开辟的空间是n的多少倍
12.冒泡排序
排序的本质是什么?
排序是比较大小。非常错误的。
排序是比较优先级,然后进行位置交换。
在比较大小的时候,例如从小到大排列,数字越小,优先级越高。
代码:
public static boolean compare(int a, int b) {
if (a > b) return true;
return false;
}
public static void change(int[] arr, int aIndex, int bIndex) {
int temp = arr[aIndex];
arr[aIndex] = arr[bIndex];
arr[bIndex] = temp;
}
public static void sort(int[] arr) {
//一边比较,一边交换
for (int i = 0 ; i < arr.length ; i ++) {
for (int j = 0 ; j < arr.length - i - 1 ; j ++) {
if (compare(arr[j], arr[j + 1])) {
change(arr, j, j + 1);
}
}
}
}
测试:
public static void main(String[] args) {
int[] arr = new int[]{
3,1,9,6,5,2,7,8};
sort(arr);
for (int val : arr) {
System.out.println(val);
}
}
结果:
13.选择排序
选择一个极值,进行排序
代码:
public static boolean compare(int a, int b) {
if (a > b) return true;
return false;
}
public static void change(int[] arr, int aIndex, int bIndex) {
int temp = arr[aIndex];
arr[aIndex] = arr[bIndex];
arr[bIndex] = temp;
}
public static void sort(int[] arr) {
//找到最大的,然后进行交换。
for (int i = 0 ; i < arr.length ; i ++) {
int maxIndex = 0;
for (int j = 0 ; j < arr.length - i ; j ++) {
if (compare(arr[j], arr[maxIndex])) {
maxIndex = j;
}
}
change(arr, maxIndex, arr.length - 1 - i);
}
}
测试:
public static void main(String[] args) {
int[] arr = new int[]{
3,1,9,6,5,2,7,8};
sort(arr);
for (int val : arr) {
System.out.println(va