数据结构和算法
参考视频:https://www.bilibili.com/video/av79558651
1、数组
封装一个集合MyOrderArray
package cn.xiaov;
/**
* 有序集合
*/
public class MyOrderArray {
private int[] arr;
//有效数据的长度
private int size;
public MyOrderArray() {
arr = new int[10];
}
public MyOrderArray(int maxSize) {
arr = new int[maxSize];
}
/**
* 添加数据
*
* @param value
*/
public void add(int value) {
//判断数组长度是否够
if (size>=arr.length){
//不够,扩容
int[] temp = new int[arr.length * 2];
System.arraycopy(arr,0,temp,0,size);
arr=temp;
}
//够,直接按顺序加数据
int i;
for (i = 0; i < size; i++) {
if (value < arr[i]) {
break;
}
}
for (int j = size; j > i; j--) {
arr[j] = arr[j - 1];
}
arr[i++] = value;
}
/**
* 显示所有
*/
public String toString() {
String temp = "";
for (int i = 0; i < size; i++) {
temp += arr[i] + " ";
}
return this.getClass().getSimpleName() + " = [ " + temp + "]";
}
/**
* 根据下标查询数据
*
* @param index
*/
public int get(int index) {
if (index >= size || index < 0) {
throw new ArrayIndexOutOfBoundsException();
} else {
return arr[index];
}
}
/**
* 二分法查找数据
* 返回数据的下标
*
* @param value
* @return
*/
public int binarySearch(int value) {
int middle = 0;
int low = 0;
int pow = size;
while (true) {
middle = (low + pow) / 2;
if (arr[middle] == value) {
return middle;
} else if (low > pow) {
throw new IndexOutOfBoundsException();
} else if (arr[middle] < value) {
low = middle + 1;
} else if (arr[middle] > value) {
pow = middle - 1;
}
}
}
/**
* 判断:如果包含指定元素,则返回 true;否者返回false
*
* @param value
* @return
*/
public boolean contains(int value) {
for (int i = 0; i < size; i++) {
if (value == arr[i]) {
return true;
}
}
return false;
}
/**
* 根据索引删除数据
*
* @param index
* @return
*/
public int remove(int index) {
if (index < 0 || index >= size) {
throw new ArrayIndexOutOfBoundsException();
} else {
for (int i = index; i < size; i++) {
arr[i] = arr[i + 1];
}
size--;
}
return arr[index];
}
/**
* 根据索引修改数据,返回原数据
*
* @param index
* @param newValue
* @return
*/
public int set(int index, int newValue) {
int temp;
if (index >= size || index < 0) {
throw new ArrayIndexOutOfBoundsException();
} else {
temp = arr[index];
arr[index] = newValue;
return temp;
}
}
}
以上代码对于int[]的数组封装成MyArray集合,进行增删查改等操作。
2、排序
冒泡排序
只要是小值,就向上交换冒泡到最上面
package cn.xiaov;
public class BubbleSort {
public static void sort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = arr.length - 1; j > i; j--) {
if (arr[j] < arr[j - 1]) {
//交换位置
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
}
}
}
}
}
选择排序
将最小值的下标赋值给k,将第k个和第一个交换
public class SelectionSort {
public static void sort(int[] arr) {
int k = 0;
for (int i = 0; i < arr.length - 1; i++) {
k = i;
for (int j = i; j < arr.length; j++) {
if (arr[j] < arr[k]) {
k = j;
}
}
int temp;
temp = arr[k];
arr[k] = arr[i];
arr[i] = temp;
}
}
}
插入排序
从第二个数起,要是小于前面一个数,存入temp;前面所有大于temp的数右移一位。
package cn.xiaov;
public class InsertSort {
public static void sort(int[] arr){
int temp=0;
for (int i = 1; i < arr.length; i++) {
temp=arr[i];
int j=i;
while (j>0&&temp<arr[j-1]){
arr[j]=arr[j-1];
j--;
}
arr[j]=temp;
}
}
}
3、栈和队列
栈
先进后出,后进先出
package cn.xiaov.stack;
public class MyStack {
//底层是一个数组
private int[] arr;
//栈顶元素下标
private int top;
public MyStack(){
arr=new int[10];
top=-1;
}
public MyStack(int maxSize){
arr=new int[maxSize];
top=-1;
}
/**
* 添加
* @param value
*/
public void push(int value){
arr[++top]=value;
}
/**
* 移除数据
* @return
*/
public long pop(){
return arr[top--];
}
/**
* 查看数据
* @return
*/
public int peek(){
return arr[top];
}
/**
* 判断是否为空
* @return
*/
public boolean isEmpty(){
return top==-1;
}
public boolean isFull(){
return top==arr.length-1;
}
}
队列
先进先出,后进后出
package cn.xiaov.queue;
/**
* 循环队列
*/
public class MyCycleQueue {
//底层是一个数组
private int[] arr;
//有效数据
private int elements;
//队头
private int front;
//队尾
private int end;
public MyCycleQueue() {
arr = new int[10];
elements = 0;
front = 0;
end = -1;
}
public MyCycleQueue(int maxSize) {
arr = new int[maxSize];
elements = 0;
front = 0;
end = -1;
}
/**
* 插入数据,从队尾插入
*
* @param value
*/
public void insert(int value) {
//如果队尾为数组最后一个值了,那么循环回-1
if (end==arr.length-1){
end=-1;
}
arr[++end] = value;
elements++;
}
/**
* 移除数据,从队头移除
*
* @return
*/
public int remove() {
int value=arr[front++];
if (front==arr.length){
front=0;
}
elements--;
return value;
}
/**
* 查看数据,从队头查看
*
* @return
*/
public int peek() {
/* if (front==arr.length){
front=0;
}*/
if (elements==0){
throw new ArrayIndexOutOfBoundsException();
}
return arr[front];
}
/**
* 判断是否为空
*
* @return
*/
public boolean isEmpty() {
return elements == 0;
}
/**
* 判断是否满了
* @return
*/
public boolean isFull() {
return elements == arr.length;
}
}
4、链表
5、递归
将大问题分解成小问题,不断调用自身。只需要给最小的值赋值就好了。
显示100以内的数
package cn.xiaov.recursion;
public class Demo1 {
public static void main(String[] args) {
test(100);
}
private static void test(int i) {
if (i==0){
return;
}
System.out.print(i+" ");
test(i-1);
}
}
斐波那契数列
package cn.xiaov.fibonacci;
public class FibonacciDemo {
public static int getNum(int num){
if (num==1){
return 0;
}
if (num==2){
return 1;
}
return getNum(num-1)+getNum(num-2);
}
}
汉诺塔(3底座,移动盘子)
package cn.xiaov.hanoitower;
/**
* 移动盘子
*/
public class HanoiTower {
public static void doTower(int topN, //移动的盘子数
char from, //初始的盘子位置
char inter, //中间位置
char to){ //目标位置
if (topN==1){
System.out.println("盘子1从"+from+"到"+to);
}else{
doTower(topN-1,from,to,inter);
System.out.println("盘子"+topN+"从"+from+"到"+to);
doTower(topN-1, inter, from, to);
}
}
}
6、希尔排序
计算最大间隔,分开进行插入排序,然后减小间隔,再次排序。
package cn.xiaov;
/**
* 希尔排序
*/
public class ShellSort {
public static void sort(int[] arr) {
int h = 1;
//计算最大的长度
while (h < arr.length / 3) {
h = h * 3 + 1;
}
while (h > 0) {
//插入排序
int temp = 0;
for (int i = h; i < arr.length; i++) {
temp = arr[i];
int j = i;
while (j > h-1 && temp < arr[j - h]) {
arr[j] = arr[j - h];
j -= h;
}
arr[j] = temp;
}
//减小间隔
h = (h - 1) / 3;
}
for (var temp :
arr) {
System.out.print(temp+" ");
}
}
}
7、快速排序
选择最右边的数字为基数,左指针移动,大于基数交换位置,移动右指针,小于基数交换位置,一趟完成。
调用自身,基数左边,基数右边。递归到最后。
package cn.xiaov;
public class QuickSort {
public static int[] sort(int[] arr, int start, int end) {
int leftPtr = start;
int rightPtr = end;
int point = arr[end];
while (leftPtr < rightPtr) {
//左指针向右移动,直到大于point停止
while (leftPtr < rightPtr && arr[leftPtr] < point) {
leftPtr++;
}
//右指针向左移动,直到小于point停止
while (leftPtr < rightPtr && arr[rightPtr] > point) {
rightPtr--;
}
//两指针相遇,或者值相同
if (leftPtr < rightPtr && arr[leftPtr] == arr[rightPtr]) {
leftPtr++;
} else {
//交换leftPtr和rightPtr
int temp;
temp = arr[leftPtr];
arr[leftPtr] = arr[rightPtr];
arr[rightPtr] = temp;
}
}
if (leftPtr - 1 > start) {
sort(arr, start, leftPtr - 1);
}
if (rightPtr + 1 < end) {
sort(arr, rightPtr + 1, end);
}
return arr;
}
}
8、二叉树
package cn.xiaov.binarytree;
/**
* 二叉树的节点
*/
public class Node {
//数据项,索引
public int data;
//数据项
public String sData;
//左子节点
public Node leftChild;
//右子节点
public Node rightChild;
public Node(int value,String sData){
this.data=value;
this.sData=sData;
}
}
在Tree.java中定义根节点
//根节点
public Node root;
插入节点
从根节点开始插入节点,通过判断大小,小于节点值则为左子节点,大于节点值则为右子节点。
/**
* 增加子节点
*
* @param value
* @param sData
*/
public void insert(int value, String sData) {
Node node = new Node(value, sData);
//引用当前节点
Node current = root;
//引用父节点
Node parent;
//如果树为空
if (root == null) {
root = node;
} else {
while (current != null) {
parent = current;
if (current.data > value) {
current = current.leftChild;
if (current == null) {
parent.leftChild = node;
}
} else {
current = current.rightChild;
if (current == null) {
parent.rightChild = node;
}
}
}
}
}
查找节点
从根节点开始查找,如果查找的节点值比当前值小,查找其左子节点,否则查找其右子节点。
/**
* 查找子节点
* @param value
* @return
*/
public boolean find(int value) {
//最开始当前节点为root
Node current = root;
while (current.data != value) {
//当前值大于查找值,查找左子树
if (current.data > value) {
current = current.leftChild;
} else {
//当前值小于查找值,查找右子树
current = current.rightChild;
}
if (current == null) {
return false;
}
}
return true;
}
二叉树的遍历
前序遍历
/**
* 前序遍历
*/
public void frontOrder(Node localNode) {
if (localNode!=null){
//访问根节点
System.out.println(localNode.data+", "+localNode.sData);
//前序遍历左子树
frontOrder(localNode.leftChild);
//前序遍历右子树
frontOrder(localNode.rightChild);
}
}
中序遍历
中序遍历后,顺序为从小到大
/**
* 中序遍历
* @param localNode
*/
public void inOrder(Node localNode) {
if (localNode!=null){
//中序遍历左子树
inOrder(localNode.leftChild);
//访问根节点
System.out.println(localNode.data+", "+localNode.sData);
//中序遍历右子树
inOrder(localNode.rightChild);
}
}
后序遍历
/**
* 后序遍历
*
* @param localNode
*/
public void afterOrder(Node localNode) {
if (localNode != null) {
//后序遍历左子树
afterOrder(localNode.leftChild);
//后序遍历右子树
afterOrder(localNode.rightChild);
//访问根节点
System.out.println(localNode.data + ", " + localNode.sData);
}
}
删除节点
/**
* 删除子节点
*
* @param value
*/
public boolean delete(int value) {
//引用当前节点,从根节点开始
Node current = root;
//引用父节点
Node parent = root;
//是否为左子节点
boolean isLeftChild = true;
//循环查找子节点
while (current.data != value) {
parent = current;
//当前值大于查找值,查找左子树
if (current.data > value) {
current = current.leftChild;
isLeftChild = true;
} else {
//当前值小于查找值,查找右子树
current = current.rightChild;
isLeftChild = false;
}
if (current == null) {
//查找不到这个子节点
return false;
}
}
//查找到了节点
if (current.leftChild == null && current.rightChild == null) {
//如果该节点没有子节点
if (current == root) {
root = null;
} else if (isLeftChild) {
parent.leftChild = null;
} else {
parent.rightChild = null;
}
} else if (current.leftChild == null) {
//该节点有且只有一个子节点,且是右节点
if (current == root) {
root = current.rightChild;
} else if (isLeftChild) {
parent.leftChild = current.rightChild;
} else {
parent.rightChild = current.rightChild;
}
} else if (current.rightChild == null) {
//该节点有且只有一个子节点,且是左节点
if (current == root) {
root = current.leftChild;
} else if (isLeftChild) {
parent.leftChild = current.leftChild;
} else {
parent.rightChild = current.leftChild;
}
} else {
//该节点有两个节点
Node successor = getSuccessor(current);
if (successor == root) {
root = null;
} else if (isLeftChild) {
parent.leftChild = successor;
} else {
parent.rightChild = successor;
}
}
return true;
}
/**
* 返回中序后继节点
* @param delNode
* @return
*/
private Node getSuccessor(Node delNode) {
Node successor = delNode;
Node successorParent = delNode;
Node current = delNode.rightChild;
while (current != null) {
successorParent = successor;
successor = current;
current = current.leftChild;
}
if (delNode.rightChild != successor) {
successorParent.leftChild = successor.rightChild;
successor.rightChild = delNode.rightChild;
}else{
successor.leftChild=successorParent.leftChild;
}
return successor;
}
9、红黑树
红黑规则:
- 每个节点不是红色就是黑色
- 根总是黑色的
- 如果节点是红色的,则它的子节点必须是黑色的
- 从根节点到叶子节点的每条路径,必须包含相同数目的黑色节点
10、哈希表
分为两部分:key 和data
int类型key
info.java
package cn.xiaov.key_int;
public class Info {
private int key;
private String name;
public int getKey() {
return key;
}
public void setKey(int key) {
this.key = key;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Info(int key, String name) {
this.key = key;
this.name = name;
}
}
HashTable.java
package cn.xiaov.key_int;
public class HashTable {
public Info[] arr;
public HashTable(){
arr =new Info[100];
}
/**
* 指定初始化大小
* @param value
*/
public HashTable(int value){
arr=new Info[value];
}
/**
* 插入数据
* @param info
*/
public void insert(Info info){
arr[info.getKey()]=info;
}
/**
* 查找
* @param key
* @return
*/
public Info find(int key){
return arr[key];
}
/**
* 删除数据
* @param key
*/
public void delete(int key){
arr[key]=null;
}
}
String类型key
开放地址法
底层是一个数组,压缩后如果冲突,hashValue++找寻相邻下一个数组值。
info.java
package cn.xiaov.key_string;
public class Info {
private String key;
private String name;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Info(String key, String name) {
this.key = key;
this.name = name;
}
}
HashTable.java
package cn.xiaov.key_string;
import java.math.BigInteger;
/**
* 开放地址法
*/
public class HashTable {
public Info[] arr;
public HashTable() {
arr = new Info[100];
}
/**
* 指定初始化大小
*
* @param value
*/
public HashTable(int value) {
arr = new Info[value];
}
/**
* 插入数据
*
* @param info
*/
public void insert(Info info) {
//获取关键字
String key = info.getKey();
int hashValue = hashCode(key);
while (arr[hashValue] != null && arr[hashValue].getName() != null) {
hashValue++;
hashValue %= arr.length;
}
arr[hashValue] = info;
}
/**
* 查找
*
* @param key
* @return
*/
public Info find(String key) {
int hashValue = hashCode(key);
//连续区间
while (arr[hashValue] != null) {
if (arr[hashValue].getKey().equals(key)) {
return arr[hashValue];
}
hashValue++;
hashValue %= arr.length;
}
//没找到
return null;
}
/**
* 删除数据
*
* @param key
*/
public boolean delete(String key) {
Info info = find(key);
if (info == null) {
return false;
} else {
arr[hashCode(info.getKey())] = null;
return true;
}
}
/**
* 将字母哈希化
* @param key
* @return
*/
public int hashCode(String key) {
/* int hashValue = 0;
int pow27 = 1;
for (int i = key.length() - 1; i >= 0; i--) {
int letter;
letter = key.charAt(i) - 96;
hashValue += letter * pow27;
pow27 *= 27;
}*/
BigInteger hashValue = new BigInteger("0");
BigInteger pow27 = new BigInteger("1");
for (int i = key.length() - 1; i >= 0; i--) {
BigInteger letter = new BigInteger(String.valueOf(key.charAt(i) - 96));
hashValue = hashValue.add(letter.multiply(pow27));
pow27 = pow27.multiply(new BigInteger("27"));
}
return hashValue.mod(new BigInteger(String.valueOf(arr.length))).intValue();
}
}
链地址法
底层是一个链表数组,压缩后如果冲突,直接链表添加,查找链表就好了。
info.java同上
Node.java
package cn.xiaov.chainaddress;
/**
* 链结点
*/
public class Node {
//数据域
public Info info;
//节点域 指向后一个节点 指针域
public Node next;
//指向前一个节点
public Node previous;
public Node(Info info){
this.info=info;
}
}
LinkList.java
package cn.xiaov.chainaddress;
/**
* 单链表
*/
public class LinkList {
//头节点
private Node first;
/**
* 添加头节点
*
* @param info
*/
public void insertFirst(Info info) {
Node node = new Node(info);
if (first == null) {
first = node;
} else {
node.next = first;
first = node;
}
}
/**
* 根据数据域删除一个节点,并返回该节点
* 如果没有这个数据则返回null
*
* @param key
* @return
*/
public Node deleteNode(String key) {
Node current = first;
Node previous = first;
//该节点为头节点
if (key.equals(first.info.getKey())) {
first = first.next;
return current;
}
while (current != null) {
if (key.equals(current.info.getKey())) {
previous.next = current.next;
return current;
}
previous = current;
current = current.next;
}
return null;
}
/**
* 根据数据域查找,如果有返回true
*
* @param key
* @return
*/
public Node find(String key) {
Node current = first;
while (current != null) {
if (key.equals(current.info.getKey())) {
return current;
}
current = current.next;
}
return null;
}
@Override
public String toString() {
Node current = first;
String temp = "";
while (current != null) {
temp += current + " ";
current = current.next;
}
return temp;
}
}
HashTable.java
package cn.xiaov.chainaddress;
import java.math.BigInteger;
/**
* 链地址法
*/
public class HashTable {
public LinkList[] arr;
public HashTable() {
arr = new LinkList[100];
}
/**
* 指定初始化大小
*
* @param value
*/
public HashTable(int value) {
arr = new LinkList[value];
}
/**
* 插入数据
*
* @param info
*/
public void insert(Info info) {
//获取关键字
String key = info.getKey();
int hashValue = hashCode(key);
if (arr[hashValue]==null){
arr[hashValue]=new LinkList();
}
arr[hashValue].insertFirst(info);
}
/**
* 查找
*
* @param key
* @return
*/
public Info find(String key) {
int hashValue = hashCode(key);
if (arr[hashValue]!=null) {
return arr[hashValue].find(key).info;
}else{
return null;
}
}
/**
* 删除数据
*
* @param key
*/
public boolean delete(String key) {
Info info = find(key);
if (info == null) {
return false;
} else {
arr[hashCode(info.getKey())] = null;
return true;
}
}
/**
* 将字母哈希化
* @param key
* @return
*/
public int hashCode(String key) {
BigInteger hashValue = new BigInteger("0");
BigInteger pow27 = new BigInteger("1");
for (int i = key.length() - 1; i >= 0; i--) {
BigInteger letter = new BigInteger(String.valueOf(key.charAt(i) - 96));
hashValue = hashValue.add(letter.multiply(pow27));
pow27 = pow27.multiply(new BigInteger("27"));
}
return hashValue.mod(new BigInteger(String.valueOf(arr.length))).intValue();
}
}