概述
数据结构
数据存储方式 | 连续存储<=>数组<=>空间固定 连续存储<=>节点+指针<=>空间不固定 |
数据存储结构 | 线性表、树、图 |
存储结构基本功能 | 增删改查 |
线性表
数组的操作
增:索引增(头部增,尾部增)
删:索引删(头部删,尾部删)
改:索引改(头部改,尾部改)
查:索引查(头部查,尾部查),遍历查
整体操作:顺序遍历,逆序遍历,排序,反转,截取,合并
查找数值:排序+二分查找,遍历查找
链式操作
增:遍历增(头部增,尾部增)
删:遍历删(头部删,尾部删)
改:遍历改(头部改,尾部改)
查:遍历查(头部查,尾部查)
整体操作:顺序遍历,逆序遍历,反转,截取,合并
树
二叉树的操作
增加:遍历增
删除:遍历删
修改:遍历改
查找:遍历查
整体操作:前序遍历、中序遍历、后序遍历 、层次遍历、线索化
图
图的操作
深度优先遍历,广度优先遍历
常见算法
穷举
生硬的穷举:列出所有解法,再选出最优解
机智的穷举:列出所有解法的过程中,剔除一定不是最优的解,再选出最优解
拆分-合成
大问题拆分成解法相同的小问题,解法相同的小问题合并成大问题
小问题一步步发展成大问题
缓存
求大问题们的最优解,往往要用到小问题们的最优解
缓存小问题们的最优解,供大问题们使用
二维DP数组可以用来存储小问题们的最优解,DP[i][x]会用到DP[i-1][y]的数据
线性结构
普通数组
操作
增:索引增(头部增,尾部增)
删:索引删(头部删,尾部删)
改:索引改(头部改,尾部改)
查:索引查(头部查,尾部查),遍历查
整体操作:顺序遍历,逆序遍历,排序,反转,截取,合并
查找数值:排序+二分查找,遍历查找
代码
//选择排序
public static void choiceSort(int[] arr){
for(int circlenum=0;circlenum<arr.length-1;circlenum++){
int lastIndex=arr.length-1-circlenum;
int maxIndex=0;
for(int i=0;i<arr.length-circlenum;i++){
if(arr[i]>arr[maxIndex]){
maxIndex=i;
}
}
int temp=arr[maxIndex];
arr[maxIndex]=arr[lastIndex];
arr[lastIndex]=temp;
}
}
//冒泡排序
public static void bubbleSort(int[] arr){
for(int num=0;num<arr.length-1;num++){
//arr.length-1 ====> arr.length-1-num 是一步优化
for(int i=0;i<arr.length-num-1;i++){
if(arr[i]>arr[i+1]){
int temp=arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
}
}
//快速排序
public static void fastSort(int[] arr,int begin,int end){
int length=end-begin+1;
if(length<=1){
return;
}
if(length==2){
if(arr[begin]>arr[end]){
int temp=arr[begin];
arr[begin]=arr[end];
arr[end]=temp;
}
return;
}
int beginPoint=begin+1;
int endPoint=end;
while(beginPoint<endPoint){
while(arr[beginPoint]<=arr[begin]){
beginPoint++;
if(beginPoint>endPoint){
break;
}
}
while(arr[endPoint]>arr[begin]){
endPoint--;
if(beginPoint>endPoint){
break;
}
}
if(beginPoint>endPoint){
break;
}
int temp=arr[beginPoint];
arr[beginPoint]=arr[endPoint];
arr[endPoint]=temp;
}
int temp=arr[begin];
arr[begin]=arr[endPoint];
arr[endPoint]=temp;
fastSort(arr,begin,endPoint-1);
fastSort(arr,beginPoint,end);
}
//插入排序
public static void insertSort(int[] arr){
for(int num=0;num<arr.length-1;num++){
int insertNum=arr[num+1];
for(int i=num;i>=0;i--){
if(arr[i]>insertNum){
arr[i+1]=arr[i];
if(i==0){
arr[0]=insertNum;
}
}else{
arr[i+1]=insertNum;
break;
}
}
}
}
//归并排序
public static void combineSort(int[] arr,int begin,int end){
int length=end-begin+1;
System.out.println(123);
if(length<=1){
return;
}
int middle=(begin+end)/2;
combineSort(arr,begin,middle);
combineSort(arr,middle+1,end);
int firstPoint=begin;
int secondPoint=middle+1;
int[] tempArr=new int[length];
int i=0;
while(firstPoint<=middle&&secondPoint<=end){
if(arr[firstPoint]<arr[secondPoint]){
tempArr[i]=arr[firstPoint];
firstPoint++;
}
else{
tempArr[i]=arr[secondPoint];
secondPoint++;
}
i++;
}
while(firstPoint<=middle){
tempArr[i]=arr[firstPoint];
firstPoint++;
}
while(secondPoint<=end){
tempArr[i]=arr[secondPoint];
secondPoint++;
}
for(int index=0;index<tempArr.length;index++){
arr[begin]=tempArr[index];
begin++;
}
}
//堆排序
public static void heapSort(int[] arr){
int endIndex=arr.length-1;
while(endIndex>0){
toBigHeap(arr,endIndex);
int temp=arr[endIndex];
arr[endIndex]=arr[0];
arr[0]=temp;
endIndex--;
}
}
public static void toBigHeap(int[] arr,int endIndex){
int leafIndex=(endIndex+1)/2-1;
while(leafIndex>-1){
int maxChild;
if(leafIndex*2+2>endIndex){
maxChild=leafIndex*2+1;
}else{
maxChild=arr[leafIndex*2+1]>arr[leafIndex*2+2]?leafIndex*2+1:leafIndex*2+2;
}
if(arr[maxChild]>arr[leafIndex]){
int temp=arr[maxChild];
arr[maxChild]=arr[leafIndex];
arr[leafIndex]=temp;
}
leafIndex--;
}
}
//二分查找
public static int binarySearch(int[] arr,int left,int right,int value){
int lenght=right-left+1;
if(lenght==1){
if(arr[left]==value){
return left;
}else{
return -1;
}
}
int middle=(left+right)/2;
if(arr[middle]==value){
return middle;
}
else if(arr[middle]>value){
return binarySearch(arr,left,middle-1,value);
}
else{
return binarySearch(arr,middle+1,right,value);
}
}
复杂度(数组长度为n)
比较n次、n-1次:时间复杂度为O(n)
比价n^2次、n(n-1)次:时间复杂对为O(n^2)
kO(n)=O(kn)
2^k=n===>k=log(2)n
T(n)=2*T(n/2)+O(n) //n-1个数字与选中数组比较n-1次,分成两组
T(n)=4*T(n/4)+2O(n/2)+O(n)
T(n)=2^k*T(n/2^k)+2^k*O(n/2^k)+...+4O(n/4)+2O(n/2)+O(n)
T(n)=nT(1)+log(2)n*O(n)
时间复杂度:T(n)=O(nlogn)
排序
排序的功能:无序数组变成有序数组
实现排序遇到的难点
①循环时,不好确定终止条件,多一次或少一次,都有可能造成错误。
②递归、循环的最后一步,有时需要额外处理。
我的建议
①多重循环,先实现内层一次循环,再逐步加码。
单链表
操作
增:遍历增(头增,尾增)
删:遍历删(头删,尾删)
改:遍历改
查:遍历查
整体操作:顺序遍历,反转
代码
class HeadNode{
String symbo1;
Node nextNode=null;
HeadNode(String symbol){
this.symbo1=symbol;
}
//链表长度
public int getLength(){
int num=0;
Node node=this.nextNode;
while(node!=null){
node=node.nextNode;
num++;
}
return num;
}
//增
public void insert(Node insertNode,int position){
if(position>getLength()-1){
System.out.println("可以插入的索引:"+0+"~"+(getLength()-1));
return;
}
if(position==0){
unshift(insertNode);
}
Node node=this.nextNode;
for(int i=0;i<position-1;i++){
node=node.nextNode;
}
insertNode.nextNode=node.nextNode;
node.nextNode=insertNode;
}
//删
public Node remove(int position){
if(position>getLength()-1){
System.out.println("可以删除的索引:"+0+"~"+(getLength()-1));
return null;
}
if(position==0){
return shift();
}
Node node=this.nextNode;
for(int i=0;i<position-1;i++){
node=node.nextNode;
}
Node removeNode=node.nextNode;
node.nextNode=removeNode.nextNode;
return removeNode;
}
//改
public void update(int position,int num){
if(position>getLength()-1){
System.out.println("可以更新的索引:"+0+"~"+(getLength()-1));
return;
}
Node node=this.nextNode;
for(int i=0;i<position;i++){
node=node.nextNode;
}
node.num=num;
}
//查
public Node find(int position){
Node node=this.nextNode;
for(int i=0;i<position;i++){
if(node!=null){
node=node.nextNode;
}
}
return node;
}
public void push(Node pushNode){
if(this.nextNode==null){
this.nextNode=pushNode;
return;
}
Node node=this.nextNode;
while(node.nextNode!=null){
node=node.nextNode;
}
node.nextNode=pushNode;
}
public Node pop(){
Node node=this.nextNode;
if(node==null){
return node;
}
if(node.nextNode==null){
this.nextNode=null;
return node;
}
while(node.nextNode.nextNode!=null){
node=node.nextNode;
}
Node popNode=node.nextNode;
node.nextNode=null;
return popNode;
}
public void unshift(Node unshiftNode){
unshiftNode.nextNode=this.nextNode;
this.nextNode=unshiftNode;
}
public Node shift(){
if(this.nextNode==null){
return null;
}
Node shiftNode=this.nextNode;
this.nextNode=shiftNode.nextNode;
return shiftNode;
}
//遍历
public void traverse(){
if(this.nextNode==null){
System.out.println("链表已空");
}
Node node=this.nextNode;
while(node!=null){
System.out.print(node.num+" ");
node=node.nextNode;
}
}
//反转
public void reverseLink(){
Node head=this.nextNode;
Node middle=head.nextNode;
Node tail=middle.nextNode;
head.nextNode=null;
while(tail!=null){
middle.nextNode=head;
head=middle;
middle=tail;
tail=tail.nextNode;
}
middle.nextNode=head;
this.nextNode=middle;
}
}
class Node{
int num;
Node nextNode=null;
Node(int num){
this.num=num;
}
}
三指针反转图示
p2=p1.nextNode;
p3=p2.nextNode;
p2.nextNode=p1;
p1=p2;
p2=p3;
p3=p3.nextNode;
数组栈
操作
尾增,尾删
代码
class ArrayStack{
private int maxSize;
private String[] stack;
public int top=-1;
public ArrayStack(int maxSize){
this.maxSize=maxSize;
stack=new String[this.maxSize];
}
public boolean isFull(){
return this.top==this.maxSize-1;
}
public boolean isEmpty(){
return this.top==-1;
}
public void push(String num){
if(this.top==this.maxSize-1){
System.out.println("栈已满");
return;
}
top++;
stack[top]=num;
}
public String pop(){
if(this.top==-1){
return "empty";
}
String value=stack[this.top];
this.top--;
return value;
}
public String getLast(){
if(this.top==-1){
return "empty";
}
String value=stack[this.top];
return value;
}
public void showStack(){
for(int i=0;i<=this.top;i++){
System.out.print(this.stack[i]+" ");
}
System.out.println();
}
}
链表栈
略
线性数组队列
描述
①列头head固定为0;列尾tail在变化
②队列长度length=tail-head+1=tail+1
③元素入列,列尾后移一位(tail++),元素放到列尾的位置。
④元素出列,列头元素出列,其余元素向前移动一位。
图示
举例
①head=0,tail=5,length=6。有6个元素
②head=0,tail=0,length=1。有1个元素
③head=0,tail=9,length=9。有10个元素,已满
④head=0,tail=-1,length=0,。为空
缺点
列头head固定。元素出列,数组整体需要移动,十分消耗性能。
环形数组队列
描述
①列头head、列尾tail都在变化。
②长度length=tail-head+1
③元素入列,列尾后移一位(tail++),元素放到列尾位置。
④元素出列,列头元素出列,列头后移一位(head++)。
图示
举例
①head=2,tail=5,length=4,有4个元素
②head=5,tail=12,length=8,有8个元素
④head=0,tail=-1,length=0,为空。
⑤head=0,tail=9,length=10,已满。
⑥head=10,tail=9,length=0,为空。
代码
class ArrayQueue {
private int head;
private int tail;
private TreeNode[] arr;
private int maxSize;
public ArrayQueue(int maxSize) {
this.maxSize=maxSize;
arr = new TreeNode[this.maxSize];
head = 0;
tail = -1;
}
public int getLength() {
int length=tail-head+1;
return length;
}
public void push(TreeNode treeNode) {
if(getLength()==10){
System.out.println("队列已满");
return;
}
tail++;
arr[tail%10]=treeNode;
}
public TreeNode shift(){
if(getLength()==0){
System.out.println("队列已空");
return null;
}
TreeNode treeNode=arr[head%10];
head++;
return treeNode;
}
public void showQueue() {
int start=head;
int end=tail;
while(start>end){
System.out.print(arr[start%10]);
start++;
}
}
}
class TreeNode{
public int num;
public TreeNode leftNode;
public TreeNode rightNode;
}
链表队列
描述
我推荐链表使用头结点,可以把方法全部放到头结点中,一般节点只需存储指针和数据。
图示
代码
public class singleLinkDemo {
public static void main(String[] args){
HeadNode myQueue=new HeadNode("001");
Node node;
for(int i=0;i<10;i++){
myQueue.push(i);
}
myQueue.showQueue();
for(int i=0;i<5;i++){
if((node=myQueue.shift())!=null){
System.out.println(node.num+"出列");
}
}
myQueue.showQueue();
for(int i=0;i<10;i++){
myQueue.push(i);
}
myQueue.showQueue();
}
}
class HeadNode{
String symbo1;
Node nextNode=null;
Node lastNode=null;
HeadNode(String symbol){
this.symbo1=symbol;
}
public int getLength(){
int length=0;
Node node=this.nextNode;
while(node!=null){
length++;
node=node.nextNode;
}
return length;
}
public void push(int num){
Node pushNode=new Node(num);
if(this.nextNode==null){
this.nextNode=pushNode;
return;
}
Node node=this.nextNode;
while(node.nextNode!=null){
node=node.nextNode;
}
node.nextNode=pushNode;
}
public Node shift(){
if(this.nextNode==null){
return null;
}
Node shiftNode=this.nextNode;
this.nextNode=shiftNode.nextNode;
return shiftNode;
}
public void showQueue(){
if(this.nextNode==null){
System.out.println("队列已空");
return;
}
Node node=this.nextNode;
System.out.println("队列内容");
while(node!=null){
System.out.print(node.num+" ");
node=node.nextNode;
}
System.out.println();
}
}
class Node{
int num;
Node nextNode=null;
Node(int num){
this.num=num;
}
}
运行结果
队列内容
0 1 2 3 4 5 6 7 8 9
0出列
1出列
2出列
3出列
4出列
队列内容
5 6 7 8 9
队列内容
5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
树结构
普通二叉树
操作
增:遍历增
删:遍历删
改:遍历改
查:遍历查
整体操作:前序遍历,中序遍历,后序遍历,层次遍历
代码
public class TreeTest {
public static void main(String[] args) {
TreeNode node1=new TreeNode(1,"一一");
TreeNode node2=new TreeNode(2,"二二");
TreeNode node3=new TreeNode(3,"张三");
TreeNode node4=new TreeNode(4,"李四");
TreeNode node5=new TreeNode(5,"王五");
TreeNode node6=new TreeNode(6,"六六");
node1.leftNode=node2;
node1.rightNode=node3;
node2.leftNode=node4;
node2.rightNode=node5;
node3.leftNode=node6;
System.out.println("前序遍历");
node1.frontTraverse(node1);
System.out.println();
System.out.println("中序遍历");
node1.middleTraverse(node1);
System.out.println();
System.out.println("后序遍历");
node1.afterTraverse(node1);
System.out.println();
System.out.println("层次遍历");
node1.levelTraverse(node1);
System.out.println();
System.out.println("前序遍历查找");
TreeNode getNode=node1.frontSearch(5,node1);
System.out.println(getNode.num+getNode.name);
node1.deleteNode(2,node1);
System.out.println("层次遍历");
node1.levelTraverse(node1);
}
}
class TreeNode{
public int num;
public String name;
public TreeNode leftNode;
public TreeNode rightNode;
TreeNode(int num,String name){
this.num=num;
this.name=name;
}
//前序遍历
public void frontTraverse(TreeNode treeNode){
System.out.print(treeNode.num+" ");
if(treeNode.leftNode!=null){
frontTraverse(treeNode.leftNode);
}
if(treeNode.rightNode!=null){
frontTraverse(treeNode.rightNode);
}
}
//中序遍历
public void middleTraverse(TreeNode treeNode){
if(treeNode.leftNode!=null){
middleTraverse(treeNode.leftNode);
}
System.out.print(treeNode.num+" ");
if(treeNode.rightNode!=null){
middleTraverse(treeNode.rightNode);
}
}
//后序遍历
public void afterTraverse(TreeNode treeNode){
if(treeNode.leftNode!=null){
afterTraverse(treeNode.leftNode);
}
if(treeNode.rightNode!=null){
afterTraverse(treeNode.rightNode);
}
System.out.print(treeNode.num+" ");
}
//层次遍历
public void levelTraverse(TreeNode treeNode){
ArrayQueue treeNodeQueue=new ArrayQueue(20);
treeNodeQueue.push(treeNode);
while(treeNodeQueue.getLength()>0){
TreeNode shiftTreeNode=treeNodeQueue.shift();
if(shiftTreeNode!=null){
System.out.print(shiftTreeNode.num+" ");
if(shiftTreeNode.leftNode!=null){
treeNodeQueue.push(shiftTreeNode.leftNode);
}
if(shiftTreeNode.rightNode!=null){
treeNodeQueue.push(shiftTreeNode.rightNode);
}
}
}
}
//前序查找
public TreeNode frontSearch(int num,TreeNode node){
if(node.num==num){
return node;
}
if(node.leftNode!=null){
TreeNode leftNode=frontSearch(num,node.leftNode);
if(leftNode!=null){
return leftNode;
}
}
if(node.rightNode!=null){
TreeNode rightNode=frontSearch(num,node.rightNode);
if(leftNode!=null){
return rightNode;
}
}
return null;
}
//遍历删除
public void deleteNode(int num,TreeNode node){
if(node==null){
return;
}
if(node.leftNode!=null){
if(node.leftNode.num==num){
node.leftNode=null;
}else{
deleteNode(num,node.leftNode);
}
}
if(node.rightNode!=null){
if(node.rightNode.num==num){
node.rightNode=null;
}else{
deleteNode(num,node.rightNode);
}
}
}
}
class ArrayQueue {
private int head;
private int tail;
private TreeNode[] arr;
private int maxSize;
public ArrayQueue(int maxSize) {
this.maxSize=maxSize;
arr = new TreeNode[this.maxSize];
head = 0;
tail = -1;
}
public int getLength() {
int length=tail-head+1;
return length;
}
public void push(TreeNode treeNode) {
if(getLength()==10){
System.out.println("队列已满");
return;
}
tail++;
arr[tail%10]=treeNode;
}
public TreeNode shift(){
if(getLength()==0){
System.out.println("队列已空");
return null;
}
TreeNode treeNode=arr[head%10];
head++;
return treeNode;
}
public void showQueue() {
int start=head;
int end=tail;
while(start>end){
System.out.print(arr[start%10]);
start++;
}
}
}
运行结果
前序遍历
1 2 4 5 3 6
中序遍历
4 2 5 1 6 3
后序遍历
4 5 2 6 3 1
层次遍历
1 2 3 4 5 6
前序遍历查找
5王五
层次遍历
1 3 6
- 先序遍历:根左右;根节点已知,左子树大小和右子树大小未知
- 中序遍历:左根右;根节点未知,根节点左边是左子树,根节点右边是右子树
- 后序遍历:左右根;根节点已知,左子树大小和右子树大小未知
- 完整树和子树的规律相同
顺序存储树
简单介绍
存储方式:数组
树的结构:完全二叉树
代码
public class Test {
public static void main(String[] args) {
int[] arr={0,1,2,3,4,5,6,7};
OrderBinaryTree orderBinaryTree=new OrderBinaryTree(arr);
orderBinaryTree.frontTraverse(0);
System.out.println();
orderBinaryTree.middleTraverse(0);
System.out.println();
orderBinaryTree.afterTraverse(0);
}
}
class OrderBinaryTree{
private int[] arr;
public OrderBinaryTree(int[] arr){
this.arr=arr;
}
public void frontTraverse(int index){
if(arr==null||arr.length==0){
System.out.println("不存在此节点");
}
System.out.print(arr[index]+" ");
if((index*2+1)<arr.length){
frontTraverse(index*2+1);
}
if((index*2+2)<arr.length){
frontTraverse(index*2+2);
}
}
public void middleTraverse(int index){
if(arr==null||arr.length==0){
System.out.println("不存在此节点");
}
if((index*2+1)<arr.length){
middleTraverse(index*2+1);
}
System.out.print(arr[index]+" ");
if((index*2+2)<arr.length){
middleTraverse(index*2+2);
}
}
public void afterTraverse(int index){
if(arr==null||arr.length==0){
System.out.println("不存在此节点");
}
if((index*2+1)<arr.length){
afterTraverse(index*2+1);
}
if((index*2+2)<arr.length){
afterTraverse(index*2+2);
}
System.out.print(arr[index]+" ");
}
}
运行结果
0 1 3 7 4 2 5 6
7 3 1 4 0 5 2 6
7 3 4 1 5 6 2 0
哈夫曼树
简单介绍
带权路径长度最短树
代码
public class Test {
public static void main(String[] args) {
int[] arr={2,4,3,6,1,8};
Node node=Node.huffmanTree(arr);
Node.middleTraverse(node);
}
}
class Node{
int num;
Node leftNode;
Node rightNode;
Node(int num){
this.num=num;
}
public static void bubbleSort(Node[] arr,int endPoint){
for(int num=0;num<endPoint;num++){
for(int i=0;i<endPoint-num;i++){
if(arr[i].num<arr[i+1].num){
Node temp=arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
}
}
public static Node huffmanTree(int[] arr){
Node[] nodeArr=new Node[arr.length];
for(int i=0;i<nodeArr.length;i++){
nodeArr[i]=new Node(arr[i]);
}
int endPoint= nodeArr.length-1;
while(endPoint>0){
bubbleSort(nodeArr,endPoint);
Node rootNode=new Node(nodeArr[endPoint].num+nodeArr[endPoint-1].num);
rootNode.leftNode=nodeArr[endPoint];
rootNode.rightNode=nodeArr[endPoint-1];
nodeArr[endPoint-1]=rootNode;
endPoint--;
}
return nodeArr[0];
}
public static void middleTraverse(Node node){
if(node.leftNode!=null){
middleTraverse(node.leftNode);
}
System.out.print(node.num+" ");
if(node.rightNode!=null){
middleTraverse(node.rightNode);
}
}
}
运行结果
4 10 1 3 2 6 3 24 6 14 8
图示
二叉排序树
简单介绍
节点左侧<节点<节点右侧
代码
public class Test {
public static void main(String[] args) {
int[] arr={2,4,3,6,1,8};
Node[] nodeArr=new Node[arr.length];
for (int i=0;i<arr.length;i++){
nodeArr[i]=new Node(arr[i]);
}
for(int i=1;i<nodeArr.length;i++){
Node.insertNode(nodeArr[0],nodeArr[i]);
}
Node.middleTraverse(nodeArr[0]);
Node.removeNode(nodeArr[0],3);
System.out.println();
Node.middleTraverse(nodeArr[0]);
Node.removeNode(nodeArr[0],6);
System.out.println();
Node.middleTraverse(nodeArr[0]);
}
}
class Node{
int num;
Node leftNode;
Node rightNode;
Node(int num){
this.num=num;
}
public static void insertNode(Node rootNode,Node addNode){
if(rootNode.num>addNode.num){
if(rootNode.leftNode==null){
rootNode.leftNode=addNode;
}
else{
insertNode(rootNode.leftNode,addNode);
}
}
else{
if(rootNode.rightNode==null){
rootNode.rightNode=addNode;
}
else{
insertNode(rootNode.rightNode,addNode);
}
}
}
public static Node findNode(Node rootNode,int num){
if(rootNode==null){
return null;
}
if(rootNode.num==num){
return rootNode;
}
if(rootNode.num>num){
return findNode(rootNode.leftNode,num);
}
else{
return findNode(rootNode.rightNode,num);
}
}
public static Node findParentNode(Node rootNode,Node childNode){
if(rootNode.leftNode==childNode||rootNode.rightNode==childNode){
return rootNode;
}
if(rootNode.leftNode!=null){
Node node= findParentNode(rootNode.leftNode,childNode);
if(node!=null){
return node;
}
}
if(rootNode.rightNode!=null){
Node node=findParentNode(rootNode.rightNode,childNode);
if(node!=null){
return node;
}
}
return null;
}
public static Node removeNode(Node rootNode,int num){
Node node=findNode(rootNode,num);
Node parentNode=findParentNode(rootNode,node);
if(parentNode.leftNode==node){
if(node.leftNode==null&&node.rightNode==null){
parentNode.leftNode=null;
}
if(node.leftNode!=null&&node.rightNode==null){
parentNode.leftNode=node.leftNode;
node.leftNode=null;
}
if(node.leftNode==null&&node.rightNode!=null){
parentNode.leftNode=node.rightNode;
node.rightNode=null;
}
if(node.leftNode!=null&&node.rightNode!=null){
Node maxNode=getMaxNode(node.leftNode);
if(maxNode==node.leftNode){
parentNode.leftNode=maxNode;
maxNode.rightNode=node.rightNode;
}
else{
removeNode(node.leftNode,maxNode.num);
maxNode.leftNode=node.leftNode;
maxNode.rightNode=node.rightNode;
parentNode.leftNode=maxNode;
}
}
}
else{
if(node.leftNode==null&&node.rightNode==null){
parentNode.rightNode=null;
}
if(node.leftNode!=null&&node.rightNode==null){
parentNode.rightNode=node.leftNode;
node.leftNode=null;
}
if(node.leftNode==null&&node.rightNode!=null){
parentNode.rightNode=node.rightNode;
node.rightNode=null;
}
if(node.leftNode!=null&&node.rightNode!=null){
Node maxNode=getMaxNode(node.leftNode);
if (maxNode==node.leftNode){
parentNode.rightNode=maxNode;
maxNode.rightNode=node.rightNode;
}
else{
removeNode(node.leftNode,maxNode.num);
maxNode.leftNode=node.leftNode;
maxNode.rightNode=node.rightNode;
parentNode.rightNode=maxNode;
}
}
}
return node;
}
public static Node getMaxNode(Node rootNode){
if(rootNode==null){
return null;
}
Node maxNode=rootNode;
while(maxNode.rightNode!=null){
maxNode=maxNode.rightNode;
}
return maxNode;
}
public static void middleTraverse(Node node){
if(node.leftNode!=null){
middleTraverse(node.leftNode);
}
System.out.print(node.num+" ");
if(node.rightNode!=null){
middleTraverse(node.rightNode);
}
}
}
运行结果
1 2 3 4 6 8
1 2 4 6 8
1 2 4 8
要点分析
节点B的取代者:右min和左max可以作为B节点的取代者
最值节点的结构:最值节点是叶节点或者单子树节点
平衡二叉树
图结构
普通图
操作
深度优先遍历,广度优先遍历
代码
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
String[] arr={"A","B","C","D","E"};
Graph graph=new Graph(5);
for(int i=0;i<arr.length;i++){
graph.insertVertex(arr[i]);
}
graph.insertEdge(0,3,1);
graph.insertEdge(0,2,1);
graph.insertEdge(1,2,1);
graph.insertEdge(1,3,1);
graph.insertEdge(1,4,1);
graph.showEdges();
graph.deepSearch(0);
System.out.println();
graph.breadthSearch(0);
}
}
class Graph{
String[] vertexs;
int vertexNum;
int[][] edges;
int edgeNum;
Graph(int capacity){
vertexs=new String[capacity];
edges=new int[capacity][capacity];
vertexNum=0;
edgeNum=0;
}
public void insertVertex(String vertex){
vertexs[vertexNum]=vertex;
vertexNum++;
}
public void insertEdge(int v1,int v2,int weight){
edges[v1][v2]=weight;
edges[v2][v1]=weight;
edgeNum++;
}
public int getWeight(int v1,int v2){
return edges[v1][v2];
}
public void showEdges(){
for(int i=0;i<edges.length;i++){
for(int j=0;j<edges.length;j++){
System.out.print(edges[i][j]+" ");
}
System.out.println();
}
}
public void deepSearch(int beginIndex){
boolean[] isVisited=new boolean[vertexs.length];
ArrayList arrayList=new ArrayList<>();
int currentVertexIndex;
int visitedNum;
visitedNum=0;
currentVertexIndex=beginIndex;
isVisited[currentVertexIndex]=true;
visitedNum++;
System.out.print(vertexs[currentVertexIndex]+" ");
while(visitedNum<5){
int flag=0;
for(int i=0;i<isVisited.length;i++){
if(isVisited[i]==false&&getWeight(currentVertexIndex,i)==1){
flag=1;
arrayList.add(currentVertexIndex);
currentVertexIndex=i;
isVisited[currentVertexIndex]=true;
visitedNum++;
System.out.print(vertexs[currentVertexIndex]+" ");
break;
}
}
if(flag==0){
currentVertexIndex=(int)arrayList.remove(arrayList.size()-1);
}
}
}
public void breadthSearch(int beginIndex){
boolean[] isVisited=new boolean[vertexs.length];
ArrayList arrayList=new ArrayList<>();
int currentVertexIndex;
int visitedNum;
visitedNum=0;
currentVertexIndex=beginIndex;
isVisited[currentVertexIndex]=true;
visitedNum++;
System.out.print(vertexs[currentVertexIndex]+" ");
while(visitedNum<5){
int flag=0;
for(int i=0;i<isVisited.length;i++){
if(isVisited[i]==false&&getWeight(currentVertexIndex,i)==1){
flag=1;
arrayList.add(i);
isVisited[i]=true;
visitedNum++;
System.out.print(vertexs[i]+" ");
}
}
if(flag==0){
currentVertexIndex=(int)arrayList.remove(0);
}
}
}
}
运行结果
0 0 1 1 0
0 0 1 1 1
1 1 0 0 0
1 1 0 0 0
0 1 0 0 0
A C B D E
A C D B E
算法
字符串穷举对比
字符串A是字符串B的子串吗?
策略
匹配不成功,字符串A后移1位
举例
第一次比较 | ||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 |
字符串B | a | a | b | a | b | c |
字符串A | a | b | c | |||
结果 | 不匹配 |
第二次比较 | ||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 |
字符串B | a | a | b | a | b | c |
字符串A | a | b | c | |||
结果 | 不匹配 |
第三次比较 | ||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 |
字符串B | a | a | b | a | b | c |
字符串A | a | b | c | |||
结果 | 不匹配 |
第四次比较 | ||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 |
字符串B | a | a | b | a | b | c |
字符串A | a | b | c | |||
结果 | 匹配 |
KMP算法
策略
匹配不成功,字符串A后移1位及以上
例1
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |||||
字符串B | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | |||||
字符串A | a | f | a | a | f | a | b | c | |||||||
结果 | 字符串A的第8位不匹配 |
操作①
不后移 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串B | a | f | a | a | f | a | b | ! c |
字符串A | a | f | a | a | f | a | b | c |
后移1位 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串B | a | f | a | a | f | a | b | ! c |
字符串A | a | f | a | a | f | a | b | |
结果 | 前7位有不匹配 |
后移2位 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串B | a | f | a | a | f | a | b | ! c |
字符串A | a | f | a | a | f | a | ||
结果 | 前7位有不匹配 |
后移3位 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串B | a | f | a | a | f | a | b | ! c |
字符串A | a | f | a | a | f | |||
结果 | 前7位有不匹配 |
后移4位 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串B | a | f | a | a | f | a | b | ! c |
字符串A | a | f | a | a | ||||
结果 | 前7位有不匹配 |
后移5位 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串B | a | f | a | a | f | a | b | ! c |
字符串A | a | f | a | |||||
结果 | 前7位有不匹配 |
后移6位 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串B | a | f | a | a | f | a | b | ! c |
字符串A | a | f | ||||||
结果 | 前7位有不匹配 |
后移7位 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串B | a | f | a | a | f | a | b | ! c |
字符串A | a | |||||||
结果 | 前7位没有不匹配 => 字符串A可以后移7位 |
分析
- 字符串A第8位不匹配,通过字符串A的前7位可以算出:字符串A能后移7位
- 字符串A第1~7位不匹配,必然也能算出,而且操作是操作①的子集
操作①加强
不后移 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串B | a | f | a | a | f | a | b | ! c |
字符串A | a | f | a | a | f | a | b | c |
后移1位 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串B | a | f | a | a | f | a | b | ! c |
字符串A | a | f | a | a | f | a | b |
第2位:前1位没有不匹配,后移1位
第3位:前2位有不匹配
……
第8位:前7位有不匹配
后移2位 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串B | a | f | a | a | f | a | b | ! c |
字符串A | a | f | a | a | f | a |
第3位:前2位没有不匹配,后移2位
第4位:前3位没有不匹配,后移2位
第5位:前4位有不匹配
……
第8位:前7位有不匹配
后移3位 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串B | a | f | a | a | f | a | b | ! c |
字符串A | a | f | a | a | f |
第5位:前4位没有不匹配,后移3位
第6位:前5位没有不匹配,后移3位
第7位:前6位没有不匹配,后移3位
第8位:前7位有不匹配
后移4位 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串B | a | f | a | a | f | a | b | ! c |
字符串A | a | f | a | a |
第8位:前7位有不匹配
后移5位 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串B | a | f | a | a | f | a | b | ! c |
字符串A | a | f | a |
第8位:前7位有不匹配
后移6位 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串B | a | f | a | a | f | a | b | ! c |
字符串A | a | f |
第8位:前7位有不匹配
后移7位 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串B | a | f | a | a | f | a | b | ! c |
字符串A | a |
第8位:前7位没有不匹配,后移7位
第n位不匹配时 | ||||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
字符串A | a | f | a | a | f | a | b | c |
后移 | 1 | 1 | 2 | 2 | 3 | 3 | 3 | 7 |
例2
位数 | 1 | 2 | 3 | 4 | 5 | 6 |
字符串A | a | b | b | a | b | b |
不后移 | ||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 |
字符串B | a | b | b | a | b | ! b |
字符串A | a | b | b | a | b | b |
后移1位 | ||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 |
字符串B | a | b | b | a | b | ! b |
字符串A | a | b | b | a | b |
第2位:前1位没有不匹配,后移1位
第3位:前2位有不匹配
……
第6位:前5位有不匹配
后移2位 | ||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 |
字符串B | a | b | b | a | b | ! b |
字符串A | a | b | b | a |
第3位:前2位没有不匹配,后移2位
第4位:前3位有不匹配
……
第6位:前5位有不匹配
后移3位 | ||||||
位数 | 1 | 2 | 3 | 4 | 5 | 6 |
字符串B | a | b | b | a | b | ! b |
字符串A | a | b | b |
第4位:前3位没有不匹配,后移3位
第5位:前4位没有不匹配,后移3位
第6位:前5位没有不匹配,后移3位
位数 | 1 | 2 | 3 | 4 | 5 | 6 |
字符串A | a | b | b | a | b | b |
后移位数 | 1 | 1 | 2 | 3 | 3 | 3 |
代码
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
char[] strA1={'a','f','a','a','f','a','b','c'};
char[] strA2={'a','b','b','a','b','b'};
KMP(strA1);
KMP(strA2);
}
public static void KMP(char[] originStrA){
char[] strA=new char[originStrA.length+1];
for(int i=0;i<originStrA.length;i++){
strA[i]=originStrA[i];
}
strA[strA.length-1]='?';
char[] strB=Arrays.copyOf(strA,strA.length);
int[] toMove=new int[originStrA.length];
toMove[0]=1;
int beginIndex=0;
int moveLength=0;
while(moveLength<strA.length){
int noMatchIndex=-1;
moveLength++;
beginIndex++;
for(int i=strA.length-1;i>0;i--){
strA[i]=strA[i-1];
}
for(int i=beginIndex;i<strA.length;i++){
if(strA[i]!=strB[i]){
noMatchIndex=i;
break;
}
}
for(int i=beginIndex;i<toMove.length;i++){
if(toMove[i]==0&&noMatchIndex>=i){
toMove[i]=moveLength;
}
}
}
System.out.println(new String(originStrA)+" 不匹配时移动的位数:");
for(int i=0;i<toMove.length;i++){
System.out.print(toMove[i]+" ");
}
System.out.println();
}
}
运行结果
afaafabc 不匹配时移动的位数:
1 1 2 2 3 3 3 7
abbabb 不匹配时移动的位数:
1 1 2 3 3 3
普利姆算法
public class Test {
public static void main(String[] args) {
String[] vertexs={"A","B","C","D","E","F","G"};
int[][] edgesWeight={
{100,5,7,100,100,100,2},
{5,100,100,9,100,100,3},
{7,100,100,100,8,100,100},
{100,9,100,100,100,4,100},
{100,100,8,100,100,5,4},
{100,100,100,4,5,100,6},
{2,3,100,100,4,6,100},
};
Graph graph=new Graph(vertexs,edgesWeight);
graph.showEdges();
graph.minTree(0);
}
}
class Graph {
String[] vertexs;
int[][] edgesWeight;
Graph(String[] vertexs,int[][] edgesWeight){
this.vertexs=vertexs;
this.edgesWeight=edgesWeight;
}
public void showEdges() {
for (int i = 0; i < edgesWeight.length; i++) {
for (int j = 0; j < edgesWeight.length; j++) {
System.out.print(edgesWeight[i][j] + " ");
}
System.out.println();
}
}
public void minTree(int beginIndex){
boolean[] isVisited=new boolean[vertexs.length];
isVisited[beginIndex]=true;
for(int isVisitedNum=1;isVisitedNum<isVisited.length;isVisitedNum++){
int index1=-1;
int index2=-1;
int minweight=100;
for(int i=0;i<isVisited.length;i++){
if(isVisited[i]==true){
for(int j=0;j<isVisited.length;j++){
if(isVisited[j]==false&&edgesWeight[i][j]<minweight){
minweight=edgesWeight[i][j];
index1=i;
index2=j;
}
}
}
}
isVisited[index2]=true;
System.out.println(
vertexs[index1]
+"->"
+vertexs[index2]
+":"+edgesWeight[index1][index2]
);
}
}
}
A->G:2
G->B:3
G->E:4
E->F:5
F->D:4
A->C:7
克鲁斯卡尔算法
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
String[] vertexs={"A","B","C","D","E","F","G"};
int[][] edges={
{0,1,5},{0,2,7},{0,6,2},{1,3,9},{1,6,3},
{2,4,8},{3,5,4},{4,5,5},{4,6,4},{5,6,6}
};
Graph graph=new Graph(vertexs,edges);
graph.sortEdges();
graph.minTree();
}
}
class Graph {
String[] vertexs;
int[][] edges;
Graph(String[] vertexs,int[][] edges) {
this.vertexs=vertexs;
this.edges=edges;
}
public void sortEdges(){
for(int num=0;num<edges.length-1;num++){
for(int i=0;i<edges.length-num-1;i++){
if(edges[i][2]>edges[i+1][2]){
int[] temp=edges[i];
edges[i]=edges[i+1];
edges[i+1]=temp;
}
}
}
}
public void minTree(){
ArrayList treeList=new ArrayList<>();
for(int i=0;i< vertexs.length;i++){
ArrayList tree=new ArrayList<>();
tree.add(i);
treeList.add(tree);
}
for(int i=0;i<edges.length;i++){
int addedgeNum=0;
int vertex1=edges[i][0];
int vertex2=edges[i][1];
int weight=edges[i][2];
int tree1Index=getTreeIndex(treeList,vertex1);
int tree2Index=getTreeIndex(treeList,vertex2);
if(tree1Index!=tree2Index){
ArrayList tree1;
ArrayList tree2;
if(tree1Index>tree2Index){
tree1=(ArrayList)treeList.remove(tree1Index);
tree2=(ArrayList)treeList.remove(tree2Index);
}
else{
tree2=(ArrayList)treeList.remove(tree2Index);
tree1=(ArrayList)treeList.remove(tree1Index);
}
tree1.addAll(tree2);
treeList.add(tree1);
System.out.println(vertexs[vertex1]+" "+vertexs[vertex2]+" "+weight);
addedgeNum++;
}
if(addedgeNum== vertexs.length-1){
break;
}
}
}
public int getTreeIndex(ArrayList treeList,int vertex){
for(int i=0;i<treeList.size();i++){
ArrayList tree=(ArrayList) treeList.get(i);
for(int j=0;j<tree.size();j++){
if(vertex==(int)tree.get(j)){
return i;
}
}
}
return -1;
}
public void showEdges(){
for(int i=0;i<edges.length;i++){
System.out.println(edges[i][0]+" "+edges[i][1]+" "+edges[i][2]);
}
}
}
A G 2
B G 3
D F 4
E G 4
E F 5
A C 7
迪杰斯特拉算法
已有解法
public class Test {
public static void main(String[] args) {
final int M=1000;
String[] vertexs={"A","B","C","D","E","F","G"};
int[][] edgesWeight={
{0,5,7,M,M,M,2},
{5,0,M,9,M,M,3},
{7,M,0,M,8,M,M},
{M,9,M,0,M,4,M},
{M,M,8,M,0,5,4},
{M,M,M,4,5,0,6},
{2,3,M,M,4,6,0},
};
Graph graph=new Graph(vertexs,edgesWeight);
graph.shortestPath(0);
}
}
class Graph {
String[] vertexs;
int[][] edgesWeight;
Graph(String[] vertexs,int[][] edgesWeight){
this.vertexs=vertexs;
this.edgesWeight=edgesWeight;
}
public void showEdges() {
for (int i = 0; i < edgesWeight.length; i++) {
for (int j = 0; j < edgesWeight.length; j++) {
System.out.print(edgesWeight[i][j] + " ");
}
System.out.println();
}
}
public void shortestPath(int index){
boolean[] isVisited=new boolean[vertexs.length];
int addVertex=0;
isVisited[index]=true;
int[] pathDistance=edgesWeight[index];
System.out.println(vertexs[index]+"点修改路径长度");
for(addVertex=1;addVertex<isVisited.length;addVertex++){
for(int i=0;i<pathDistance.length;i++){
System.out.print(pathDistance[i]+" ");
}
System.out.println();
int MinDistance=100;
int vertexIndex=-1;
for(int i=0;i<pathDistance.length;i++){
if(isVisited[i]==false&&pathDistance[i]<MinDistance){
MinDistance=pathDistance[i];
vertexIndex=i;
}
}
isVisited[vertexIndex]=true;
System.out.println(vertexs[vertexIndex]+"点修改路径长度");
for(int i=0;i<edgesWeight[vertexIndex].length;i++){
if(isVisited[i]==false
&&MinDistance+edgesWeight[vertexIndex][i]<pathDistance[i]
){
pathDistance[i]=MinDistance+edgesWeight[vertexIndex][i];
}
}
}
}
}
A点修改路径长度
0 5 7 1000 1000 1000 2
G点修改路径长度
0 5 7 1000 6 8 2
B点修改路径长度
0 5 7 14 6 8 2
E点修改路径长度
0 5 7 14 6 8 2
C点修改路径长度
0 5 7 14 6 8 2
F点修改路径长度
0 5 7 12 6 8 2
D点修改路径长度
穷举求最短距离
- 列出起点A到终点Z的所有通路
- 选出距离最短的一条通路
弗洛伊德算法
代码
public class Test {
public static void main(String[] args) {
String[] vertexs={"A","B","C","D","E","F","G"};
final int M=100;
int[][] edgesWeight={
{0,5,7,M,M,M,2},
{5,0,M,9,M,M,3},
{7,M,0,M,8,M,M},
{M,9,M,0,M,4,M},
{M,M,8,M,0,5,4},
{M,M,M,4,5,0,6},
{2,3,M,M,4,6,0},
};
Graph graph=new Graph(vertexs,edgesWeight);
graph.showEdges();
graph.shortestPaths();
graph.showEdges();
}
}
class Graph {
String[] vertexs;
int[][] edgesWeight;
String[][] edgesTrace;
final int M=100;
Graph(String[] vertexs,int[][] edgesWeight){
this.vertexs=vertexs;
this.edgesWeight=edgesWeight;
this.edgesTrace=new String[edgesWeight.length][edgesWeight.length];
for (int i = 0; i < edgesWeight.length; i++) {
for (int j = 0; j < edgesWeight.length; j++) {
if(edgesWeight[i][j]<100&&edgesWeight[i][j]>0){
edgesTrace[i][j]=vertexs[i]+vertexs[j];
}
}
}
}
public void shortestPaths(){
for(int i=0;i<vertexs.length;i++){
addNodeForshortestPaths(i);
}
}
public void addNodeForshortestPaths(int addNode){
for (int i = 0; i < edgesWeight.length; i++) {
for (int j = 0; j < edgesWeight.length; j++) {
if(edgesWeight[i][addNode]+edgesWeight[addNode][j]<edgesWeight[i][j]){
edgesWeight[i][j]=edgesWeight[i][addNode]+edgesWeight[addNode][j];
edgesTrace[i][j]=edgesTrace[i][addNode]+edgesTrace[addNode][j].substring(1);
}
}
}
}
public void showEdges() {
for (int i = 0; i < edgesWeight.length; i++) {
for (int j = 0; j < edgesWeight.length; j++) {
System.out.print(edgesWeight[i][j] + " ");
}
System.out.println();
}
for (int i = 0; i < edgesTrace.length; i++) {
for (int j = 0; j < edgesTrace.length; j++) {
System.out.print(edgesTrace[i][j] + " ");
}
System.out.println();
}
}
}
运行结果
0 5 7 100 100 100 2
5 0 100 9 100 100 3
7 100 0 100 8 100 100
100 9 100 0 100 4 100
100 100 8 100 0 5 4
100 100 100 4 5 0 6
2 3 100 100 4 6 0
null AB AC null null null AG
BA null null BD null null BG
CA null null null CE null null
null DB null null null DF null
null null EC null null EF EG
null null null FD FE null FG
GA GB null null GE GF null
0 5 7 12 6 8 2
5 0 12 9 7 9 3
7 12 0 17 8 13 9
12 9 17 0 9 4 10
6 7 8 9 0 5 4
8 9 13 4 5 0 6
2 3 9 10 4 6 0null AB AC AGFD AGE AGF AG
BA null BAC BD BGE BGF BG
CA CAB null CEFD CE CEF CAG
DFGA DB DFEC null DFE DF DFG
EGA EGB EC EFD null EF EG
FGA FGB FEC FD FE null FG
GA GB GAC GFD GE GF null
背包问题
问题
- 包的承重1000kg
- 物品A、B、C、D...有着不同的重量和价值
- 确保包里放入物品的总价值最大
- 物品性价比=价值/重量
三物品最优解
[100kg~200kg)总重,A、B、C三个物品。最优解100,是[A,B]
[200kg~300kg)总重,A、B、C三个物品。最优解200,不一定是[A,B]或(A,B,...]
....
[900kg~1000kg]总重,A、B、C三个物品。最优解900
不同重量区间,相同物品。最优解不具有继承关系
四物品最优解
[100kg~200kg)总重,A、B、C、D四个物品。最优解101
[200kg~300kg)总重,A、B、C、D四个物品。最优解201
....
[900kg~1000kg]总重,A、B、C、D四个物品。最优解901
最优解101一定是[D?,...三物品最优解]
...
最优解901一定是[D?,...三物品最优解]
物品的总数递增
穷举所有物品组合+优化穷举
- 创建一个集合,用来放入满足承重的解
- 物品个数降为0个
- 加入物品A,把满足承重的解放入集合
- 加入物品B,把满足承重的解放入集合
- ...
- 加入最后的物品,把满足承重的解放入集合
- 在集合A中选出最优解
包的承重递增&&物品的总数递增
持续更新各重量区间的最优解
- 包的承重降为0kg,物品的个数降为0个,各重量区间的最优解,为0
- 加入物品A,包的承重从0kg增长到1000kg,各重量区间的最优解,会发生更新
- 加入物品B,包的承重从0kg增长到1000kg,各重量区间的最优解,会发生更新
- ....
- 加入最后的物品,包的承重从0kg增长到1000kg,各重量区间的最优解,会发生更新
- 1000kg承重的解,是最优解
应用
计算中缀表达式
应用的数据结构、算法:栈
要点
①算式数组expressionArr=[num,symbol,num,symbol,...,num,"end"]
②算式数值最后一个元素会被设置为符号“end”,作为算式结束标志。
②symbol符号的权重:^>*=/>+=->end>empty。
③创建数字栈numStack、符号栈symbolStack。
④符号栈为空时,调用pop()返回符号“empty”。
⑤符号empty不会参与运算,是为了确保栈symbolStack为空时,可以把符号压入栈中。
⑥符号symbol入栈前,如果判断是end,标识运算结束,跳出循环。
过程
①算式字符串expressionStr拆分成算式数组expressionArr。
②读取expressionArr中的元素,数字num进出numStack,符号symbol进出symbolStack。
③得出结果
代码
public class Expression {
public static void main(String[] args){
Utils utils=new Utils();
String expressionStr="1+12*3/2^2-6*2";
String[] expressionArr=utils.getExpressionArr(expressionStr);
System.out.println(utils.getResult(expressionArr));
}
}
class Utils{
private int point=0;
private char[] symbolArr={'+','-','*','/','^'};
private boolean isSymbol(char symbolOrNum){
for(int i=0;i<symbolArr.length;i++){
if(symbolArr[i]==symbolOrNum){
return true;
}
}
return false;
}
private String nextSymbol(String expressionStr){
if(point==expressionStr.length()){
point=0;
return "end";
}
char symbol=expressionStr.charAt(point);
point++;
return String.valueOf(symbol);
}
private String nextNum(String expressionStr){
int start=point;
while(isSymbol(expressionStr.charAt(point))==false){
point++;
if(point==expressionStr.length()){
break;
}
}
int end=point;
String num=expressionStr.substring(start,end);
return num;
}
public String[] getExpressionArr(String expressionStr){
String[] strArr=new String[20];
String symbolOrNum="start";
int index=0;
while(symbolOrNum!="end"){
strArr[index++]=nextNum(expressionStr);
symbolOrNum=strArr[index++]=nextSymbol(expressionStr);
}
return strArr;
}
public int getWeight(String symbol){
int weight;
switch (symbol){
case "+":weight=0;break;
case "-":weight=0;break;
case "*":weight=1;break;
case "/":weight=1;break;
case "^":weight=2;break;
case "end":weight=-1;break;
default:weight=-999;break;
}
return weight;
}
public boolean canPush(String toPushSymbol,String topSymbol){
return getWeight(toPushSymbol)>=getWeight(topSymbol);
//return getWeight(toPushSymbol)>=getWeight(topSymbol) 也可以
}
public String getResult(String[] expressionArr){
ArrayStack numStack=new ArrayStack(10);
ArrayStack symbolStack=new ArrayStack(10);
int index=0;
while(true){
String pushNum=expressionArr[index++];
numStack.push(pushNum);
String topushSymbol=expressionArr[index++];
String topSymbol=symbolStack.getLast();
while(canPush(topushSymbol,topSymbol)==false){
topSymbol=symbolStack.pop();
double toPushNum=0;
double secondNum=Double.parseDouble(numStack.pop());
double firstNum=Double.parseDouble(numStack.pop());
switch (topSymbol){
case "+":toPushNum=firstNum+secondNum;break;
case "-":toPushNum=firstNum-secondNum;break;
case "*":toPushNum=firstNum*secondNum;break;
case "/":toPushNum=firstNum/secondNum;break;
case "^":toPushNum=Math.pow(firstNum,secondNum);break;
}
numStack.push(String.valueOf(toPushNum));
topSymbol=symbolStack.getLast();
}
if(topushSymbol=="end"){
break;
}
symbolStack.push(topushSymbol);
}
return numStack.pop();
}
}
class ArrayStack{
private int maxSize;
private String[] stack;
public int top=-1;
public ArrayStack(int maxSize){
this.maxSize=maxSize;
stack=new String[this.maxSize];
}
public boolean isFull(){
return this.top==this.maxSize-1;
}
public boolean isEmpty(){
return this.top==-1;
}
public void push(String num){
if(this.top==this.maxSize-1){
System.out.println("栈已满");
return;
}
top++;
stack[top]=num;
}
public String pop(){
if(this.top==-1){
return "empty";
}
String value=stack[this.top];
this.top--;
return value;
}
public String getLast(){
if(this.top==-1){
return "empty";
}
String value=stack[this.top];
return value;
}
public void showStack(){
for(int i=0;i<=this.top;i++){
System.out.print(this.stack[i]+" ");
}
System.out.println();
}
}
运行结果
-2
中缀表达式转后缀表达式
应用的数据结构、算法:栈
要点
栈symbolStack弹出符号topSymbol后,不进行计算,而是把符号topSymbol压入栈numStack。
代码
public class Expression {
public static void main(String[] args){
Utils utils=new Utils();
String expressionStr="1+12*3/2^2-6*2";
String[] expressionArr=utils.getExpressionArr(expressionStr);
ArrayStack numStack= utils.getResult(expressionArr);
for(;numStack.top>=0;){
System.out.print(numStack.pop()+" ");
}
}
}
class Utils{
private int point=0;
private char[] symbolArr={'+','-','*','/','^'};
private boolean isSymbol(char symbolOrNum){
for(int i=0;i<symbolArr.length;i++){
if(symbolArr[i]==symbolOrNum){
return true;
}
}
return false;
}
private String nextSymbol(String expressionStr){
if(point==expressionStr.length()){
point=0;
return "end";
}
char symbol=expressionStr.charAt(point);
point++;
return String.valueOf(symbol);
}
private String nextNum(String expressionStr){
int start=point;
while(isSymbol(expressionStr.charAt(point))==false){
point++;
if(point==expressionStr.length()){
break;
}
}
int end=point;
String num=expressionStr.substring(start,end);
return num;
}
public String[] getExpressionArr(String expressionStr){
String[] strArr=new String[20];
String symbolOrNum="start";
int index=0;
while(symbolOrNum!="end"){
strArr[index++]=nextNum(expressionStr);
symbolOrNum=strArr[index++]=nextSymbol(expressionStr);
}
return strArr;
}
public int getWeight(String symbol){
int weight;
switch (symbol){
case "+":weight=0;break;
case "-":weight=0;break;
case "*":weight=1;break;
case "/":weight=1;break;
case "^":weight=2;break;
case "end":weight=-1;break;
default:weight=-999;break;
}
return weight;
}
public boolean canPush(String toPushSymbol,String topSymbol){
return getWeight(toPushSymbol)>=getWeight(topSymbol);
}
public ArrayStack getResult(String[] expressionArr){
ArrayStack numStack=new ArrayStack(15);
ArrayStack symbolStack=new ArrayStack(15);
int index=0;
while(true){
String pushNum=expressionArr[index++];
numStack.push(pushNum);
String topushSymbol=expressionArr[index++];
String topSymbol=symbolStack.getLast();
while(canPush(topushSymbol,topSymbol)==false){
topSymbol=symbolStack.pop();
numStack.push(topSymbol);
topSymbol=symbolStack.getLast();
}
if(topushSymbol=="end"){
break;
}
symbolStack.push(topushSymbol);
}
return numStack;
}
}
class ArrayStack{
private int maxSize;
private String[] stack;
public int top=-1;
public ArrayStack(int maxSize){
this.maxSize=maxSize;
stack=new String[this.maxSize];
}
public boolean isFull(){
return this.top==this.maxSize-1;
}
public boolean isEmpty(){
return this.top==-1;
}
public void push(String num){
if(this.top==this.maxSize-1){
System.out.println("栈已满");
return;
}
top++;
stack[top]=num;
}
public String pop(){
if(this.top==-1){
return "empty";
}
String value=stack[this.top];
this.top--;
return value;
}
public String getLast(){
if(this.top==-1){
return "empty";
}
String value=stack[this.top];
return value;
}
public void showStack(){
for(int i=0;i<=this.top;i++){
System.out.print(this.stack[i]+" ");
}
System.out.println();
}
}
运行结果
+ - * 2 6 * / ^ 2 2 3 12 1
迷宫走法
应用的数据结构、算法:递归
问题描述
起点到终点,走法的数目
示例
问:(0,0)点是起点,(1,1)点是终点,有几种走法。不可以(0,0)->(0,1)->(0,0)这样反复横跳
解:(0,0)->(0,1)->(1,1),(0,0)->(1,0)->(1,1)。2种路径。
代码
public class PathNum {
public static void main(String[] args){
int[][] map=new int[3][3];
map[2][2]=2;
System.out.println("3*3地图,起点(0,0),终点(2,2),路径数目:");
System.out.println(getPathNum(map,0,0));
}
public static int getPathNum(int map[][],int X, int Y){
int pathNum=0;
if(X<0||X> map.length-1||Y<0||Y> map.length-1){
return pathNum;
}
if(map[X][Y]==1){
return pathNum;
}
if(map[X][Y]==2){
return 1;
}
int[][] mapClone=new int[map.length][map.length];
for(int i=0;i<map.length;i++){
mapClone[i]=map[i].clone();
}
mapClone[X][Y]=1;
pathNum=getPathNum(mapClone,X-1,Y)
+getPathNum(mapClone,X+1,Y)
+getPathNum(mapClone,X,Y-1)
+getPathNum(mapClone,X,Y+1);
return pathNum;
}
}
运行结果
3*3地图,起点(0,0),终点(2,2),路径数目:
12
八皇后
数据结构与算法
递归
思路
按行进行遍历,每一行选出一个皇后的位置。 每一行遍历完成后,需要整理出一个数组,数组中记录着其他行不能安放皇后的位置无向图
无向图有几棵树
描述
n个节点,m个边,求出有几颗树
思路
- 选择nodeA节点作为起点,以广度优先遍历的方式找出nodeA节点连通的node节点
- 如果有nodeB节点无法与nodeA节点连通,那么nodeB节点一定在另一颗树上
- 选择nodeA节点作为起点,以广度优先遍历的方式找出nodeA节点连通的node节点
- .....
实现避免重复遍历同一个节点
- 把已遍历的边从edges数组中抛出,确保不会再次遍历
- 节点A放入treeANodes数组前,先判断treeANodes是否已包含节点A
示例
let edges = [[1, 4], [2, 4], [3, 5], [3, 6], [5, 6]]
let tree = []
while(true){
if(edges.length==0){
break
}
let nodes=edges.shift()||[]
let point=0
while (true) {
for (let i = edges.length - 1; i >= 0; i--) {
if(edges[i][0]==nodes[point]){
if(!nodes.includes(edges[i][1])){
nodes.push(edges[i][1])
}
edges.splice(i, 1)
continue
}
if(edges[i][1]==nodes[point]){
if(!nodes.includes(edges[i][0])){
nodes.push(edges[i][0])
}
edges.splice(i, 1)
}
}
if (point == nodes.length - 1) {
break
}
point++
}
tree.push(nodes)
}
console.log(tree)
PS C:\Users\Administrator\Desktop\many_env\node_browser> node .\test.js
[ [ 1, 4, 2 ], [ 3, 5, 6 ] ]