源码地址
https://github.com/javanan/DataStructure
目录
时间复杂度介绍
空间复杂度介绍
递归算法与非递归算法区别和转换
折半查找/二分查找算法
链表实现
反转一个链表
直接插入排序
快速排序
选择排序
冒泡排序
线程与锁详解
二叉树的遍历
二叉排序树
图的详解
图的邻接表存储构成图
无向图的邻接表存储-深度优先搜索
无向图的邻接表存储-广度优先搜索
无向图的邻接矩阵存储-深度优先搜索
无向图的邻接矩阵存储-广度优先搜索
有向图的创建
拓扑排序-邻接矩阵存储-Kahn算法
拓扑排序-邻接矩阵存储-深度优先搜索算法
最短路径算法之Dijkstra算法(狄克斯特拉算法
ArrayList实现原理
LinkList双向实现
堆排序
归并排序
希尔排序
八大排序总结
计数排序
同时找出最大值和最小值最优算法
快速查找法,查找第k个最大的数
10亿数据查找前100个
散列表(哈希表)
求最大不重复子串
死锁
两个线程交替输出1010
关注我,一个仍存梦想的屌丝程序员,每天为你分享高质量编程博客。
image阿里云优惠券与阿里云上云教程<http://aliyun.guan2ye.com/>
时间复杂度介绍
package com.wangpos.datastructure.sort;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.wangpos.datastructure.R;
public class TimeComplexityActivity extends AppCompatActivity {
private TextView tvIntroduce;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_time_complexity);
tvIntroduce = (TextView)findViewById(R.id.introduce);
TextView tvTwo = (TextView)findViewById(R.id.tvTwo);
tvIntroduce.setText("计算机科学中,算法的时间复杂度是一个函数,它定性描述了该算法的运行时间。" +
"这是一个关于代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。" +
"使用这种方式时,时间复杂度可被称为是渐近的,它考察当输入值大小趋近无穷时的情况。\n" +
"1.一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。\n" +
"分析:随着模块n的增大,算法执行的时间的增长率和 f(n) 的增长率成正比,所以 f(n) 越小,算法的时间复杂度越低,算法的效率越高。\n" +
"2. 在计算时间复杂度的时候,先找出算法的基本操作,然后根据相应的各语句确定它的执行次数,再找出 T(n) 的同数量级(它的同数量级有以下:1,log2n,n,n log2n ,n的平方,n的三次方,2的n次方,n!),找出后,f(n) = 该数量级,若 T(n)/f(n) 求极限可得到一常数c,则时间复杂度T(n) = O(f(n))" +
"\n" +
"\n" +
"for(i=1; i<=n; ++i) c1 执行 n次\n" +
"{\n" +
" for(j=1; j<=n; ++j) c2 执行 n平方次\n" +
" {\n" +
" c[i][j] = 0; c3 //该步骤属于基本操作执行次数:n的平方次\n" +
" for(k=1; k<=n; ++k) c4 执行 n立方次\n" +
" c[i][j] += a[i][k] * b[k][j]; c5 //该步骤属于基本操作执行次数:n的三次方次\n" +
" }\n" +
"}\n" +
"则有T(n)=n^3+n^2 ,根据上面括号里的同数量级,我们可以确定 n的三次方 为T(n)的同数量级\n" +
"则有f(n)=n^3 ,然后根据 T(n)/f(n) 求极限可得到常数c\n" +
"则该算法的时间复杂度:T(n) = O(n^3) 注:n^3即是n的3次方。" +
"" +
"" +
"上面求解一个算法的时间复杂度过程,假设算法的每一步执行时间c1,c2,c3,执行的次可以通过计算的n, n,n^2,n^2,n^3 等" +
"\n" +
"T(n) = c1*n + c2*n^2 + c3*n2 + c4*n^3 + c5*n^3\n" +
"找到最高级数 ,去除常数项,就是n^3\n");
tvTwo.setText("" +
"通常说的时间复杂度就是,找到这个算法的函数,找到对应的同级数,比如 a*n^2 + b*n +c ,同级数就是n^2,时间复杂度就是n^2\n" +
"最好的情况,就是分析最少次数,比如排序时可以分析已经有序的 然后重新得出一个方程寻找方程\n" +
"最坏的情\n" +
"平均的情况就是一半一半\n");
}
}
空间复杂度
package com.wangpos.datastructure.sort;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.wangpos.datastructure.R;
public class SpaceComplexityActivity extends AppCompatActivity {
private TextView tvIntroduce;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_space_complexity);
tvIntroduce = (TextView)findViewById(R.id.introduce);
tvIntroduce.setText("空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度,记做S(n)=O(f(n))。比如直接插入排序的时间复杂度是O(n^2),空间复杂度是O(1) 。而一般的递归算法就要有O(n)的空间复杂度了,因为每次递归都要存储返回信息。一个算法的优劣主要从算法的执行时间和所需要占用的存储空间两个方面衡量。" +
"\n" +
"\n" +
"对于一个算法,时间复杂度和空间复杂度往往是相互影响的。当追求一个较好的时间复杂度时,可能会使空间复杂度的性能变差,即可能导致占用较多的存储空间;反之,当追求一个较好的空间复杂度时,可能会使时间复杂度的性能变差,即可能导致占用较长的运行时间。另外,算法的所有性能之间都存在着或多或少的相互影响。因此,当设计一个算法(特别是大型算法)时,要综合考虑算法的各项性能,算法的使用频率,算法处理的数据量的大小,算法描述语言的特性,算法运行的机器系统环境等各方面因素,才能够设计出比较好的算法。算法的时间复杂度和空间复杂度合称为算法的复杂度。");
}
}
递归算法与非递归算法区别和转换
package com.wangpos.datastructure.sort;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import com.wangpos.datastructure.R;
public class RecursionActivity extends AppCompatActivity {
private TextView mTvRecursion;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recursion);
mTvRecursion = (TextView) findViewById(R.id.tvIntroduceRecursion);
mTvRecursion.setText("" +
" 递归算法实际上是一种分而治之的方法,它把复杂问题分解为简单问题来求解。对于某些复杂问题(例如hanio塔问题)," +
"递归算法是一种自然且合乎逻辑的解决问题的方式,但是递归算法的执行效率通常比较差。因此,在求解某些问题时," +
"常采用递归算法来分析问题,用非递归算法来求解问题;另外,有些程序设计语言不支持递归,这就需要把递归算法转换为非递归算法" +
"\n" +
"\n" +
"\n" +
" 将递归算法转换为非递归算法有两种方法,一种是直接求值,不需要回溯;" +
"另一种是不能直接求值,需要回溯。前者使用一些变量保存中间结果," +
"称为直接转换法;后者使用栈保存中间结果,称为间接转换法,下面分别讨论这两种方法。" +
"" +
"" +
"\n" +
"\n" +
"\n" +
"1. 直接转换法\n" +
"直接转换法通常用来消除尾递归和单向递归,将递归结构用循环结构来替代。\n" +
"尾递归是指在递归算法中,递归调用语句只有一个,而且是处在算法的最后。例如求阶乘的递归算法:\n" +
"long fact(int n)\n" +
"{\n" +
" if (n==0) return 1;\n" +
" else return n*fact(n-1);\n" +
"}\n" +
"当递归调用返回时,是返回到上一层递归调用的下一条语句,而这个返回位置正好是算法的结束处,所以,不必利用栈来保存返回信息。对于尾递归形式的递归算法,可以利用循环结构来替代。例如求阶乘的递归算法可以写成如下循环结构的非递归算法:\n" +
"long fact(int n)\n" +
"{\n" +
" int s=0;\n" +
" for (int i=1; i\n" +
" s=s*i; //用s保存中间结果\n" +
" return s;\n" +
"}\n" +
"单向递归是指递归算法中虽然有多处递归调用语句,但各递归调用语句的参数之间没有关系,并且这些递归调用语句都处在递归算法的最后。显然,尾递归是单向递归的特例。例如求斐波那契数列的递归算法如下:\n" +
"int f(int n)\n" +
"{\n" +
" if (n= =1 | | n= =0) return 1;\n" +
" else return f(n-1)+f(n-2);\n" +
"}\n" +
"对于单向递归,可以设置一些变量保存中间结构,将递归结构用循环结构来替代。例如求斐波那契数列的算法中用s1和s2保存中间的计算结果,非递归函数如下:\n" +
"int f(int n)\n" +
"{\n" +
" int i, s;\n" +
" int s1=1, s2=1;\n" +
" for (i=3; i<=n; ++i)\n" +
" {\n" +
" \ts=s1+s2;\n" +
" \ts2=s1; // 保存f(n-2)的值\n" +
" \ts1=s; //保存f(n-1)的值\n" +
" }\n" +
" return s;\n" +
"}\n" +
"2. 间接转换法\n" +
"该方法使用栈保存中间结果,一般需根据递归函数在执行过程中栈的变化得到。其一般过程如下:\n" +
"将初始状态s0进栈\n" +
"while (栈不为空)\n" +
"{\n" +
" 退栈,将栈顶元素赋给s;\n" +
" if (s是要找的结果) 返回;\n" +
" else \n" +
" {\n" +
" 寻找到s的相关状态s1;\n" +
" 将s1进栈\n" +
" }\n" +
"}\n" +
"间接转换法在数据结构中有较多实例,如二叉树遍历算法的非递归实现、图的深度优先遍历算法的非递归实现等等" +
"" +
"\n" +
"\n" +
"递归与迭代的效率比较\n" +
"我们知道,递归调用实际上是函数自己在调用自己,而函数的调用开销是很大的,系统要为每次函数调用分配存储空间,并将调用点压栈给予以记录。" +
"而在函数调用结束后,还要释放空间,弹栈恢复断点。所以说,函数调用不仅浪费空间,还浪费时间。");
TextView tvFeature = (TextView)findViewById(R.id.tvFeature);
tvFeature.setText("" +
"1.递归在解决某些问题的时候使得我们思考的方式得以简化,代码也更加精炼,容易阅读" +
"2.递归效率低,系统要为每次函数调用分配存储空间,并将调用点压栈给予以记录" +
"3.小数据量可以选择使用");
}
}
折半查找/二分查找算法
package com.wangpos.datastructure.sort;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import com.wangpos.datastructure.R;
public class RecursionActivity extends AppCompatActivity {
private TextView mTvRecursion;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recursion);
mTvRecursion = (TextView) findViewById(R.id.tvIntroduceRecursion);
mTvRecursion.setText("" +
" 递归算法实际上是一种分而治之的方法,它把复杂问题分解为简单问题来求解。对于某些复杂问题(例如hanio塔问题)," +
"递归算法是一种自然且合乎逻辑的解决问题的方式,但是递归算法的执行效率通常比较差。因此,在求解某些问题时," +
"常采用递归算法来分析问题,用非递归算法来求解问题;另外,有些程序设计语言不支持递归,这就需要把递归算法转换为非递归算法" +
"\n" +
"\n" +
"\n" +
" 将递归算法转换为非递归算法有两种方法,一种是直接求值,不需要回溯;" +
"另一种是不能直接求值,需要回溯。前者使用一些变量保存中间结果," +
"称为直接转换法;后者使用栈保存中间结果,称为间接转换法,下面分别讨论这两种方法。" +
"" +
"" +
"\n" +
"\n" +
"\n" +
"1. 直接转换法\n" +
"直接转换法通常用来消除尾递归和单向递归,将递归结构用循环结构来替代。\n" +
"尾递归是指在递归算法中,递归调用语句只有一个,而且是处在算法的最后。例如求阶乘的递归算法:\n" +
"long fact(int n)\n" +
"{\n" +
" if (n==0) return 1;\n" +
" else return n*fact(n-1);\n" +
"}\n" +
"当递归调用返回时,是返回到上一层递归调用的下一条语句,而这个返回位置正好是算法的结束处,所以,不必利用栈来保存返回信息。对于尾递归形式的递归算法,可以利用循环结构来替代。例如求阶乘的递归算法可以写成如下循环结构的非递归算法:\n" +
"long fact(int n)\n" +
"{\n" +
" int s=0;\n" +
" for (int i=1; i\n" +
" s=s*i; //用s保存中间结果\n" +
" return s;\n" +
"}\n" +
"单向递归是指递归算法中虽然有多处递归调用语句,但各递归调用语句的参数之间没有关系,并且这些递归调用语句都处在递归算法的最后。显然,尾递归是单向递归的特例。例如求斐波那契数列的递归算法如下:\n" +
"int f(int n)\n" +
"{\n" +
" if (n= =1 | | n= =0) return 1;\n" +
" else return f(n-1)+f(n-2);\n" +
"}\n" +
"对于单向递归,可以设置一些变量保存中间结构,将递归结构用循环结构来替代。例如求斐波那契数列的算法中用s1和s2保存中间的计算结果,非递归函数如下:\n" +
"int f(int n)\n" +
"{\n" +
" int i, s;\n" +
" int s1=1, s2=1;\n" +
" for (i=3; i<=n; ++i)\n" +
" {\n" +
" \ts=s1+s2;\n" +
" \ts2=s1; // 保存f(n-2)的值\n" +
" \ts1=s; //保存f(n-1)的值\n" +
" }\n" +
" return s;\n" +
"}\n" +
"2. 间接转换法\n" +
"该方法使用栈保存中间结果,一般需根据递归函数在执行过程中栈的变化得到。其一般过程如下:\n" +
"将初始状态s0进栈\n" +
"while (栈不为空)\n" +
"{\n" +
" 退栈,将栈顶元素赋给s;\n" +
" if (s是要找的结果) 返回;\n" +
" else \n" +
" {\n" +
" 寻找到s的相关状态s1;\n" +
" 将s1进栈\n" +
" }\n" +
"}\n" +
"间接转换法在数据结构中有较多实例,如二叉树遍历算法的非递归实现、图的深度优先遍历算法的非递归实现等等" +
"" +
"\n" +
"\n" +
"递归与迭代的效率比较\n" +
"我们知道,递归调用实际上是函数自己在调用自己,而函数的调用开销是很大的,系统要为每次函数调用分配存储空间,并将调用点压栈给予以记录。" +
"而在函数调用结束后,还要释放空间,弹栈恢复断点。所以说,函数调用不仅浪费空间,还浪费时间。");
TextView tvFeature = (TextView)findViewById(R.id.tvFeature);
tvFeature.setText("" +
"1.递归在解决某些问题的时候使得我们思考的方式得以简化,代码也更加精炼,容易阅读" +
"2.递归效率低,系统要为每次函数调用分配存储空间,并将调用点压栈给予以记录" +
"3.小数据量可以选择使用");
}
}
链表实现
package com.wangpos.datastructure.sort;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.wangpos.datastructure.R;
import thereisnospon.codeview.CodeView;
import thereisnospon.codeview.CodeViewTheme;
public class EasyLinkListActivity extends AppCompatActivity {
private CodeView codeView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_easy_link_list);
codeView = (CodeView) findViewById(R.id.codeView);
codeView.setTheme(CodeViewTheme.DARK);
codeView.showCode("" +
" EasyLinkList<Integer> header = new EasyLinkList<>(1);\n" +
" EasyLinkList<Integer> second = new EasyLinkList<>(2);\n" +
" EasyLinkList<Integer> three = new EasyLinkList<>(3);\n" +
"\n" +
" header.setNext(second);\n" +
" second.setNext(three);\n" +
"\n" +
" while (header != null) {\n" +
" Log.i(\"info\", String.valueOf(header.data));\n" +
" header = header.getNext();\n" +
" }" +
"\n" +
" class EasyLinkList<Type> {\n" +
"\n" +
" private Type data;\n" +
" private EasyLinkList<Type> next;\n" +
"\n" +
" public EasyLinkList(Type dataParam) {\n" +
" this.data = dataParam;\n" +
" }\n" +
"\n" +
" public void setNext(EasyLinkList<Type> easyLinkList) {\n" +
" this.next = easyLinkList;\n" +
" }\n" +
"\n" +
" public EasyLinkList<Type> getNext() {\n" +
" return this.next;\n" +
" }\n" +
"\n" +
" }");
EasyLinkList<Integer> header = new EasyLinkList<>(1);
EasyLinkList<Integer> second = new EasyLinkList<>(2);
EasyLinkList<Integer> three = new EasyLinkList<>(3);
header.setNext(second);
second.setNext(three);
while (header != null) {
Log.i("info", String.valueOf(header.data));
header = header.getNext();
}
}
class EasyLinkList<Type> {
private Type data;
private EasyLinkList<Type> next;
public EasyLinkList(Type dataParam) {
this.data = dataParam;
}
public void setNext(EasyLinkList<Type> easyLinkList) {
this.next = easyLinkList;
}
public EasyLinkList<Type> getNext() {
return this.next;
}
}
}
反转一个链表
package com.wangpos.datastructure.sort;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.wangpos.datastructure.R;
import thereisnospon.codeview.CodeView;
import thereisnospon.codeview.CodeViewTheme;
public class EasyLinkListReverseActivity extends AppCompatActivity implements View.OnClickListener{
private CodeView codeView;
private Button btnRun;
private EasyLinkList<Integer> header;
private TextView tvResult;
private TextView tvWeidingxing;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_easy_link_list_reverse);
codeView = (CodeView) findViewById(R.id.codeView);
btnRun = (Button)findViewById(R.id.btnRun);
btnRun.setOnClickListener(this);
codeView.setTheme(CodeViewTheme.DARK);
tvResult = (TextView) findViewById(R.id.result);
// tvWeidingxing = (TextView) findViewById(R.id.tvWendingXing);
//
// tvWeidingxing.setText("");
codeView.showCode(" public EasyLinkList reverstList(EasyLinkList header) {\n" +
"\n" +
" /**\n" +
" * 保存前一个\n" +
" */\n" +
" EasyLinkList pre = null;\n" +
" /**\n" +
" * 保存当前\n" +
" */\n" +
" EasyLinkList current = header;\n" +
"\n" +
" while (current != null) { // header 表示当前\n" +
"\n" +
" EasyLinkList temp = current.getNext();\n" +
"\n" +
" /**\n" +
" * 将当前的next 设置成前一个\n" +
" */\n" +
" current.setNext(pre);\n" +
"\n" +
" /**\n" +
" * 然后当前的变成了下次的前一个\n" +
" */\n" +
" pre = current;\n" +
"\n" +
" /**\n" +
" * 下次current 是本次的 temp 也就是下一个\n" +
" */\n" +
" current = temp;\n" +
"\n" +
"\n" +
" }\n" +
"\n" +
" /**\n" +
" * 这里current 会复制为null,所以返回pre\n" +
" */\n" +
" return pre;\n" +
"// return current;\n" +
" }");
this.header = new EasyLinkList<>(1);
EasyLinkList<Integer> second = new EasyLinkList<>(2);
EasyLinkList<Integer> three = new EasyLinkList<>(3);
EasyLinkList<Integer> four = new EasyLinkList<>(4);
EasyLinkList<Integer> five = new EasyLinkList<>(5);
EasyLinkList<Integer> six = new EasyLinkList<>(6);
EasyLinkList<Integer> seven = new EasyLinkList<>(7);
EasyLinkList<Integer> eight = new EasyLinkList<>(8);
EasyLinkList<Integer> nine = new EasyLinkList<>(9);
EasyLinkList<Integer> ten = new EasyLinkList<>(10);
header.setNext(second);
second.setNext(three);
three.setNext(four);
four.setNext(five);
five.setNext(six);
six.setNext(seven);
seven.setNext(eight);
eight.setNext(nine);
nine.setNext(ten);
}
public EasyLinkList reverstList(EasyLinkList header) {
/**
* 保存前一个
*/
EasyLinkList pre = null;
/**
* 保存当前
*/
EasyLinkList current = header;
while (current != null) { // header 表示当前
EasyLinkList temp = current.getNext();
/**
* 将当前的next 设置成前一个
*/
current.setNext(pre);
/**
* 然后当前的变成了下次的前一个
*/
pre = current;
/**
* 下次current 是本次的 temp 也就是下一个
*/
current = temp;
}
/**
* 这里current 会复制为null,所以返回pre
*/
return pre;
// return current;
}
@Override
public void onClick(View view) {
header = reverstList(header);
StringBuilder sb = printString(header);
tvResult.setText(sb.toString());
}
@NonNull
private StringBuilder printString(EasyLinkList<Integer> param) {
StringBuilder sb = new StringBuilder();
sb.append("[");
EasyLinkList<Integer> header = param;
while (header != null) {
sb.append(String.valueOf(header.data));
if (header.getNext()!=null) {
sb.append(",");
}
header = header.getNext();
}
sb.append("]");
return sb;
}
class EasyLinkList<Type> {
private Type data;
private EasyLinkList<Type> next;
public EasyLinkList(Type dataParam) {
this.data = dataParam;
}
public void setNext(EasyLinkList<Type> easyLinkList) {
this.next = easyLinkList;
}
public EasyLinkList<Type> getNext() {
return this.next;
}
}
}
直接插入排序
package com.wangpos.datastructure.sort;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.wangpos.datastructure.R;
import java.util.Arrays;
import thereisnospon.codeview.CodeView;
import thereisnospon.codeview.CodeViewTheme;
public class DirectInsertSortActivity extends AppCompatActivity implements View.OnClickListener {
CodeView codeView;
private Button btnRun;
private TextView tvData;
private TextView tvResult;
private TextView tvSummary;
private TextView tvTime;
int arr[] = {23, 12, 13, 44, 65, 26, 17, 38, 59};
private TextView tvStorage;
private DataBean[]dataBeans;
private TextView tvWeidingXing;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_direct_insert_sort);
codeView = (CodeView) findViewById(R.id.codeView);
btnRun = (Button) findViewById(R.id.btnRun);
btnRun.setOnClickListener(this);
tvData = (TextView) findViewById(R.id.data);
tvResult = (TextView) findViewById(R.id.result);
tvSummary = (TextView) findViewById(R.id.summary);
codeView.setTheme(CodeViewTheme.DARK);
tvTime = (TextView) findViewById(R.id.time);
tvStorage = (TextView) findViewById(R.id.tvStorage);
tvWeidingXing = (TextView) findViewById(R.id.tvWendingXing);
tvResult.setText("");
tvData.setText(Arrays.toString(arr));
tvSummary.setText("首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,直接选择排序然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕");
tvTime.setText("O(N^2)");
tvStorage.setText("O(1)");
tvWeidingXing.setText("稳定");
codeView.showCode(" /**\n" +
" * 直接插入排序,从小到大\n" +
" *\n" +
" * @param arrays 带排序数组\n" +
" * @param <Type> 数组类型\n" +
" * @return\n" +
" */\n" +
" public <Type extends Comparable<? super Type>> Type[] directInsertSort(Type[] arrays) {\n" +
" for (int i = 0; i < arrays.length; i++) {\n" +
" for (int j = i + 1; j<arrays.length; j++) {\n" +
" Type temp = arrays[i];\n" +
" if (arrays[j].compareTo(arrays[i]) < 0) {//从小到大\n" +
" arrays[i] = arrays[j];\n" +
" arrays[j] = temp;\n" +
" }\n" +
"\n" +
" }\n" +
" }\n" +
"\n" +
" return arrays;\n" +
" }");
dataBeans = new DataBean[arr.length];
for (int i = 0; i < arr.length; i++) {
dataBeans[i] = new DataBean(arr[i]);
}
}
@Override
public void onClick(View view) {
DataBean arrays[] = directInsertSort(dataBeans);
tvResult.setText(Arrays.toString(arrays));
}
/**
* 直接插入排序,从小到大
*
* @param arrays 带排序数组
* @param <Type> 数组类型
* @return
*/
public <Type extends Comparable<? super Type>> Type[] directInsertSort(Type[] arrays) {
for (int i = 0; i < arrays.length; i++) {
for (int j = i ; j>=0; j--) {
Type temp = arrays[i];
if (arrays[j].compareTo(arrays[i]) < 0) {//从小到大
arrays[i] = arrays[j];
arrays[j] = temp;
}
}
}
return arrays;
}
public class DataBean implements Comparable {
private int data;
public DataBean(int datap) {
this.data = datap;
}
@Override
public int compareTo(@NonNull Object o) {
DataBean target = (DataBean) o;
if (data > target.data) {
return 1;
} else if (data < target.data) {
return -1;
} else {
return 0;
}
}
@Override
public String toString() {
return String.valueOf(data);
}
}
}
快速排序
package com.wangpos.datastructure.sort;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.wangpos.datastructure.R;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.wangpos.datastructure.R;
import com.wangpos.datastructure.core.BaseActivity;
import com.wangpos.datastructure.core.CodeBean;
import java.util.Arrays;
import thereisnospon.codeview.CodeView;
import thereisnospon.codeview.CodeViewTheme;
public class QuickSortActivity extends BaseActivity {
int arr[] = {23, 12, 13, 44, 65, 26, 17, 38, 59};
/**
* 记录一个23,让出自己位置,将小得放进来
*
* 23, 12, 13, 44, 65, 26, 17, 38, 59
*
* 17, 12, 13, 44, 65, 26, 17, 38, 59
*
* 17, 12, 13, 44, 65, 26, 44, 38, 59
*
* 一趟以后得 17, 12, 13, 23, 65, 26, 44, 38, 59
*
* 一趟=[17, 12, 13, 44, 65, 26, 44, 38, 59]
I/info (27284): 一趟=[17, 12, 13, 44, 65, 26, 44, 38, 59]
I/info (27284): 一趟=[13, 12, 13, 23, 65, 26, 44, 38, 59]
I/info (27284): 一趟=[12, 12, 17, 23, 65, 26, 44, 38, 59]
I/info (27284): 一趟=[12, 13, 17, 23, 59, 26, 44, 38, 59]
I/info (27284): 一趟=[12, 13, 17, 23, 38, 26, 44, 38, 65]
I/info (27284): 一趟=[12, 13, 17, 23, 26, 26, 44, 59, 65]
*/
@Override
protected void initData() {
setTitleText("快速排序");
addItem(new CodeBean("快速排序",quickSortCode));
}
@Override
protected String getTextData() {
return Arrays.toString(arr);
}
@Override
protected int getImageData() {
return 0;
}
@Override
protected String getResultData() {
sort(arr,0,arr.length-1);
return Arrays.toString(arr);
}
@Override
protected String getTimeData() {
return "O(nlogn)";
}
@Override
protected String getSpaceTimeData() {
return "";
}
@Override
protected String getWendingXingData() {
return "不稳定";
}
@Override
protected String getSummaryData() {
return "快速排序(Quicksort)是对冒泡排序的一种改进。\n" +
"快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小" +
",然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。";
}
private static int sortUnit(int[] array, int low, int high)
{
int key = array[low];//选取第一个为基准数
while (low < high)
{
/*从后向前搜索比key小的值*/
while (array[high] >= key && high > low)
--high;
/*比key小的放左边*/
array[low] = array[high];
/*从前向后搜索比key大的值,比key大的放右边*/
while (array[low] <= key && high > low)
++low;
/*比key大的放右边*/
array[high] = array[low];//
/**
* 一次的效果就是比Key小的放在了key(low)位置,再找比key大的放在high位置
*/
Log.i("info","一趟="+Arrays.toString(array));
// Log.i("info","一趟="+"low="+low +"high="+high);
}
/*左边都比key小,右边都比key大。//将key放在游标当前位置。
//此时low等于high */
/**
* 相遇位置,
* 一开始meetPosition 等于low
*/
int meetPosition = low = high;
/**
* key的位置被霸占了,所以key的位置是最终相遇的位置
*/
array[meetPosition] = key;
return meetPosition;
}
/**快速排序
*@paramarry
*@return */
public static void sort(int[] array, int low, int high)
{
if (low >= high)
return;
/*完成一次单元排序*/
int index = sortUnit(array, low, high);
/*对左边单元进行排序*/
sort(array, low, index - 1);
// /*对右边单元进行排序*/
sort(array, index + 1, high);
}
private static String quickSortCode = "private static int sortUnit(int[] array, int low, int high)\n" +
" {\n" +
" int key = array[low];//选取第一个为基准数\n" +
"\n" +
" while (low < high)\n" +
" {\n" +
" /*从后向前搜索比key小的值*/\n" +
" while (array[high] >= key && high > low)\n" +
" --high;\n" +
" /*比key小的放左边*/\n" +
" array[low] = array[high];\n" +
" /*从前向后搜索比key大的值,比key大的放右边*/\n" +
" while (array[low] <= key && high > low)\n" +
" ++low;\n" +
" /*比key大的放右边*/\n" +
" array[high] = array[low];//\n" +
"\n" +
"\n" +
" /**\n" +
" * 一次的效果就是比Key小的放在了key(low)位置,再找比key大的放在high位置\n" +
" */\n" +
"\n" +
"\n" +
" Log.i(\"info\",\"一趟=\"+Arrays.toString(array));\n" +
"// Log.i(\"info\",\"一趟=\"+\"low=\"+low +\"high=\"+high);\n" +
" }\n" +
" /*左边都比key小,右边都比key大。//将key放在游标当前位置。\n" +
" //此时low等于high */\n" +
" /**\n" +
" * 相遇位置,\n" +
" * 一开始meetPosition 等于low\n" +
" */\n" +
" int meetPosition = low = high;\n" +
"\n" +
" /**\n" +
" * key的位置被霸占了,所以key的位置是最终相遇的位置\n" +
" */\n" +
" array[meetPosition] = key;\n" +
"\n" +
" return meetPosition;\n" +
" }\n" +
" /**快速排序\n" +
" *@paramarry\n" +
" *@return */\n" +
" public static void sort(int[] array, int low, int high)\n" +
" {\n" +
" if (low >= high)\n" +
" return;\n" +
" /*完成一次单元排序*/\n" +
" int index = sortUnit(array, low, high);\n" +
" /*对左边单元进行排序*/\n" +
" sort(array, low, index - 1);\n" +
"// /*对右边单元进行排序*/\n" +
" sort(array, index + 1, high);\n" +
" }";
}
选择排序
package com.wangpos.datastructure.sort;
import com.wangpos.datastructure.core.BaseActivity;
import com.wangpos.datastructure.core.CodeBean;
import java.util.Arrays;
/**
* Created by qiyue on 2017/11/21.
*/
public class OptionSortActivity extends BaseActivity {
int arr[] = {23, 12, 13, 44, 65, 26, 17, 38, 59};
@Override
protected void initData() {
setTitleText("选择排序");
addItem(new CodeBean("选择排序",selectSortStr));
}
@Override
protected String getTextData() {
return Arrays.toString(arr);
}
@Override
protected int getImageData() {
return 0;
}
@Override
protected String getResultData() {
return Arrays.toString(selectSort(arr));
}
@Override
protected String getTimeData() {
return "O(n^2)";
}
@Override
protected String getSpaceTimeData() {
return "O(1)";
}
@Override
protected String getWendingXingData() {
return "稳定";
}
@Override
protected String getSummaryData() {
return "每一趟从待排序的记录中选出最小的元素,顺序放在已排好序的序列最后,直到全部记录排序完毕,此排序就是对冒泡的优化,不必每次都进行交换";
}
private static final String selectSortStr = "public void selectSort(int arr[]){\n" +
" for(int i = 0; i < arr.length - 1; i++) {// 做第i趟排序\n" +
" int k = i;\n" +
" for(int j = k + 1; j < arr.length; j++){// 选最小的记录\n" +
" if(arr[j] < arr[k]){\n" +
" k = j; //记下目前找到的最小值所在的位置\n" +
" }\n" +
" }\n" +
" //在内层循环结束,也就是找到本轮循环的最小的数以后,再进行交换\n" +
" if(i != k){ //交换a[i]和a[k]\n" +
" int temp = arr[i];\n" +
" arr[i] = arr[k];\n" +
" arr[k] = temp;\n" +
" }\n" +
" }\n" +
" }";
public int[] selectSort(int arr[]){
for(int i = 0; i < arr.length - 1; i++) {// 做第i趟排序
int k = i;
for(int j = k + 1; j < arr.length; j++){// 选最小的记录
if(arr[j] < arr[k]){
k = j; //记下目前找到的最小值所在的位置
}
}
//在内层循环结束,也就是找到本轮循环的最小的数以后,再进行交换
if(i != k){ //交换a[i]和a[k]
int temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
}
}
return arr;
}
}
冒泡排序
package com.wangpos.datastructure.sort;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.wangpos.datastructure.R;
import com.wangpos.datastructure.core.BaseActivity;
import com.wangpos.datastructure.core.CodeBean;
import java.lang.reflect.Array;
import java.util.Arrays;
public class BubbleSortActivity extends BaseActivity {
int arr[] = {23, 12, 13, 44, 65, 26, 17, 38, 59};
@Override
protected void initData() {
setTitleText("冒泡排序");
addItem(new CodeBean("冒泡排序",bubbleSortStr));
}
@Override
protected String getTextData() {
return Arrays.toString(arr);
}
@Override
protected int getImageData() {
return 0;
}
@Override
protected String getResultData() {
return Arrays.toString(bubbleSort(arr));
}
@Override
protected String getTimeData() {
return "O(n^2)";
}
@Override
protected String getSpaceTimeData() {
return "O(1)";
}
@Override
protected String getWendingXingData() {
return "稳定";
}
@Override
protected String getSummaryData() {
return "一次比较两个元素,如果他们的顺序错误就把他们交换过来。" +
"走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成";
}
private static final String bubbleSortStr = "public void bubbleSort(int[] a)\n" +
" {\n" +
" int temp = 0;\n" +
" /**这里的i表示比较多少次,第一次要全部进行比较得出最大的一个,第二次应该除去最大的一个,比较剩下的*/\n" +
" for (int i = a.length - 1; i > 0; --i)\n" +
" {\n" +
" for (int j = 0; j < i; ++j)\n" +
" {\n" +
" if (a[j + 1] < a[j])\n" +
" {\n" +
" temp = a[j];\n" +
" a[j] = a[j + 1];\n" +
" a[j + 1] = temp;\n" +
" }\n" +
" }\n" +
" }\n" +
" }";
public int[] bubbleSort(int[] a)
{
int temp = 0;
/**这里的i表示比较多少次,第一次要全部进行比较得出最大的一个,第二次应该除去最大的一个,比较剩下的*/
for (int i = a.length - 1; i > 0; --i)
{
for (int j = 0; j < i; ++j)
{
if (a[j + 1] < a[j])
{
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
return a;
}
}
线程与锁详解
package com.wangpos.datastructure.java;
/**
* Created by qiyue on 2017/11/17.
*/
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.wangpos.datastructure.R;
import java.util.Arrays;
import thereisnospon.codeview.CodeView;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.wangpos.datastructure.R;
import java.util.Arrays;
import thereisnospon.codeview.CodeView;
import thereisnospon.codeview.CodeViewTheme;
public class JavaThreadActivity extends AppCompatActivity implements View.OnClickListener {
CodeView codeView;
private Button btnRun;
private TextView tvData;
private TextView tvResult;
private TextView tvSummary;
private TextView tvTime;
int arr[] = {23, 12, 13, 44, 65, 26, 17, 38, 59};
private TextView tvStorage;
private com.wangpos.datastructure.sort.DirectInsertSortActivity.DataBean[] dataBeans;
private TextView tvWeidingXing;
private static TextView tvPrintField;
static Handler mHandler = new Handler();
private CodeView codeView2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.java_thread_activity);
codeView = (CodeView) findViewById(R.id.codeView);
tvData = (TextView) findViewById(R.id.data);
tvResult = (TextView) findViewById(R.id.result);
codeView.setTheme(CodeViewTheme.DARK);
codeView2 = (CodeView) findViewById(R.id.codeView2);
codeView2.setTheme(CodeViewTheme.DARK);
tvPrintField = (TextView) findViewById(R.id.printText);
tvData.setText("" +
"1.单例对象的同步方法,原理就是使用同一把锁\n" +
"2.轮训条件发\n" +
"3.wait/notify机制\n" +
"4.是利用CyclicBarrierAPI\n" +
"5.管道通信就是使用java.io.PipedInputStream\n" +
"6.利用Lock和Condition\n" +
"7.利用volatile\n" +
"8.利用AtomicInteger\n" +
"分布式系统中说的两种通信机制:共享内存机制和消息通信机制");
tvResult.setText("类锁,对象锁\n" +
"\n" +
"1.对象锁:java的所有对象都含有1个互斥锁,这个锁由JVM自动获取和释放。线程进入synchronized方法的时候获取该对象的锁," +
"当然如果已经有线程获取了这个对象的锁,那么当前线程会等待;synchronized方法正常返回或者抛异常而终止," +
"JVM会自动释放对象锁。这里也体现了用synchronized来加锁的1个好处,方法抛异常的时候,锁仍然可以由JVM来自动释放。\n" +
"\n\n" +
"2.类锁:对象锁是用来控制实例方法之间的同步,类锁是用来控制静态方法(或静态变量互斥体)之间的同步。其实类锁只是一个概念上的东西," +
"并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。我们都知道,java类可能会有很多个对象,但是只有1个Class对象," +
"也就是说类的不同实例之间共享该类的Class对象。Class对象其实也仅仅是1个java对象,只不过有点特殊而已。由于每个java对象都有1个互斥锁" +
",而类的静态方法是需要Class对象。所以所谓的类锁,不过是Class对象的锁而已。获取类的Class对象有好几种,最简单的就是MyClass.class的方式。\n" +
"\n" +
"\n" +
"\n");
codeView.showCode(" /**\n" +
" * 类锁实现一:使用 static synchronized\n" +
" * 创建两个线程,每个线程从1打印到10000 ,运行结果,t1先打印完后,t2 再打印\n" +
" * 使用类锁,不管创建多少个线程,他们都使用的是同一把锁,因为Java不管创建多少个对象,Class对象始终一个\n" +
" *\n" +
" */\n" +
" TestThread t1 = new TestThread(1);\n" +
" TestThread t2 = new TestThread(2);\n" +
" t1.start();\n" +
" t2.start();" +
"\n" +
"\n" +
" public static class TestThread extends Thread{\n" +
"\n" +
" private int number;\n" +
"\n" +
" public TestThread(int number){\n" +
" this.number = number;\n" +
" }\n" +
"\n" +
" @Override\n" +
" public void run() {\n" +
" super.run();\n" +
" test(number);\n" +
"\n" +
" }\n" +
" //类锁实现之一\n" +
" private static synchronized void test(final int number) {\n" +
" for (int i=0;i<10000;i++) {\n" +
"\n" +
" Log.i(\"info\", \"number=\" + number +\"say=\"+ i);\n" +
" }\n" +
" }\n" +
" }" +
"" +
"\n" +
"\n" +
"" +
" /**\n" +
" * 类锁实现二:使用 synchronized(TestThread.class){}\n" +
" */\n" +
" TestThread t1 = new TestThread(1);\n" +
" TestThread t2 = new TestThread(2);\n" +
" t1.start();\n" +
" t2.start();" +
"\n" +
"\n" +
"\n" +
" public static class TestThread extends Thread {\n" +
"\n" +
" private int number;\n" +
"\n" +
" public TestThread(int number) {\n" +
" this.number = number;\n" +
" }\n" +
"\n" +
" @Override\n" +
" public void run() {\n" +
" super.run();\n" +
" test(number);\n" +
"\n" +
" }\n" +
"// //类锁实现之一\n" +
"// private static synchronized void test(final int number) {\n" +
"// for (int i=0;i<10000;i++) {\n" +
"//\n" +
"// Log.i(\"info\", \"number=\" + number +\"say=\"+ i);\n" +
"// }\n" +
"// }\n" +
"\n" +
" private void test(int number) {\n" +
" synchronized (TestThread.class) {\n" +
" for (int i = 0; i < 10000; i++) {\n" +
" Log.i(\"info\", \"number=\" + number + \"say=\" + i);\n" +
" }\n" +
" }\n" +
" }\n" +
" }");
/**
* 类锁实现一:使用 static synchronized
* 创建两个线程,每个线程从1打印到10000 ,运行结果,t1先打印完后,t2 再打印
* 使用类锁,不管创建多少个线程,他们都使用的是同一把锁,因为Java不管创建多少个对象,Class对象始终一个
*
*/
// TestThread t1 = new TestThread(1);
// TestThread t2 = new TestThread(2);
// t1.start();
// t2.start();
/**
* 类锁实现二:使用 synchronized(TestThread.class){}
*/
// TestThread t1 = new TestThread(1);
// TestThread t2 = new TestThread(2);
// t1.start();
// t2.start();
codeView2.showCode(" Person_E person = new Person_E(\"AAAAA\");\n" +
" Person_E person2 = new Person_E(\"YYYYYY\");\n" +
" TestObjectLockThread t3 = new TestObjectLockThread(person);\n" +
" TestObjectLockThread t4 = new TestObjectLockThread(person2);\n" +
" t3.start();\n" +
" t4.start();\n" +
"\n" +
"\n" +
"" +
" /**\n" +
" * synchronized 修饰非静态,方法,默认获取自身对象的锁,所以在多线程的情况下\n" +
" *\n" +
" * 只有单例模式才能保证同步\n" +
" *\n" +
" */\n" +
"\n" +
" public synchronized void say()\n" +
" {\n" +
" for (int i=0;i<10000;i++) {\n" +
"\n" +
" Log.i(\"info\",name +\"说话内容=\"+ i);\n" +
" }\n" +
"\n" +
" }\n" +
"\n" +
"\n" +
" 运行发现同步失效,使用同一个Person 就会没问题"+
"\n" +
"\n" +
"\n" +
"对象锁实现二\n" +
" MyLock myLock = new MyLock();\n" +
" TestObjectLock2Thread t5 = new TestObjectLock2Thread(myLock,1);\n" +
" TestObjectLock2Thread t6 = new TestObjectLock2Thread(myLock,2);\n" +
"\n" +
" t5.start();\n" +
" t6.start();" +
" public class TestObjectLock2Thread extends Thread{\n" +
"\n" +
" private MyLock myLock;\n" +
" private int number;\n" +
" public TestObjectLock2Thread(MyLock myLock,int number){\n" +
" this.myLock = myLock;\n" +
" this.number = number;\n" +
"\n" +
" }\n" +
" @Override\n" +
" public void run() {\n" +
" super.run();\n" +
" synchronized (myLock){\n" +
" for (int i = 0;i<1000;i++){\n" +
" Log.i(\"info\", \"number=\" + number + \"say=\" + i);\n" +
" }\n" +
" }\n" +
"\n" +
" }\n" +
" }");
//
// MyLock myLock = new MyLock();
// TestObjectLock2Thread t5 = new TestObjectLock2Thread(myLock,1);
// TestObjectLock2Thread t6 = new TestObjectLock2Thread(myLock,2);
//
// t5.start();
// t6.start();
}
@Override
public void onClick(View view) {
// tvResult.setText(Arrays.toString(arrays));
}
public class TestObjectLock2Thread extends Thread{
private MyLock myLock;
private int number;
public TestObjectLock2Thread(MyLock myLock,int number){
this.myLock = myLock;
this.number = number;
}
@Override
public void run() {
super.run();
synchronized (myLock){
for (int i = 0;i<1000;i++){
Log.i("info", "number=" + number + "say=" + i);
}
}
}
}
public class TestObjectLockThread extends Thread{
private Person person;
public TestObjectLockThread(Person person){
this.person = person;
}
@Override
public void run() {
super.run();
person.say();
}
}
public static class TestThread extends Thread {
private int number;
public TestThread(int number) {
this.number = number;
}
@Override
public void run() {
super.run();
test(number);
}
// //类锁实现之一
// private static synchronized void test(final int number) {
// for (int i=0;i<10000;i++) {
//
// Log.i("info", "number=" + number +"say="+ i);
// }
// }
private void test(int number) {
synchronized (TestThread.class) {
for (int i = 0; i < 10000; i++) {
Log.i("info", "number=" + number + "say=" + i);
}
}
}
}
public class MyLock{
}
}
图的详解
## 图
##术语详解
- 图
对于图G=(V,E)
图形结构,简称“图”,是一种复杂的数据结构。图形结构中,每个结点的前驱结点数和后续结点数可以任意多个。
图是由顶点集合以及顶点间的关系集合组成的一种数据结构。
- 顶点(Vertex)
- 弧(Arc) 有向边:若从顶点Vi到Vj的边有方向,则称这条边为有向边,也称为弧。
- 弧头(初始点) 若从顶点Vi到Vj的边有方向,则称Vi弧头
- 弧尾(终结点) 若从定点Vi到Vj的边有方向,则称Vj弧尾
- 边(Edge) 无向图的连线叫边
- 无向图(Undigraph) 边没有方向的图称为无向图。 G=(V, {A})、0≤边≤n(n-1)/2
1.V是非空集合,称为顶点集。
2.E是V中元素构成的无序二元组的集合,称为边集。
- 有向图(Directed graph) 边有方向的图称为有向图 G=(V, {E})、0≤弧≤n(n-1)
- 无向完全图 (完全无向图) 若有n个顶点的无向图有n(n-1)/2 条边, 则此图为完全无向图。
- 有向完全图(完全有向图)有n个顶点的有向图有n(n-1)条边, 则此图为完全有向图。
- 稀疏图(Sparse graph) |E|远远小于|V|^2 的图,(有的书上说,当边数e<<n㏒2n时,图G称为稀疏)
- 稠密图(Dense graph) |E|接近|V|^2 的图
## 网
- 网(network) 网里面对应的边是有权值的,用以表示边的某种属性比如距离等。而图的边是没有权值的
- 权(weigh) 在处理有关图的实际问题时,往往有值的存在,比如公里数,运费,城市,口数以及电话部数等。一般这个值成为权值
- 无向网
- 有向网
- 子图(Subgraph)
- 邻接点(Adjacent)
- 度(Degree) 图中的度:所谓顶点的度(degree),就是指和该顶点相关联的边数
- 入度(Indegree) 以某顶点为弧头,终止于该顶点的弧的数目称为该顶点的入度
- 出度(Outdegree)
- 连通 在一个无向图 G 中,若从顶点i到顶点j有路径相连(当然从j到i也一定有路径),则称i和j是连通的。
如果 G 是有向图,那么连接i和j的路径中所有的边都必须同向。
如果图中任意两点都是连通的,那么图被称作连通图。
如果此图是有向图,则称为强连通图(注意:需要双向都有路径)。
- 简单路径: 是一条x到y的连通路径,x和y分别是起点和终点。当x=y时, 被称为回路。如果通路 中的边两两不同,则 是一条简单通路,否则为一条复杂通路
- 弱连通图:将有向图的所有的有向边替换为无向边,所得到的图称为原图的基图。如果一个有向图的基图是连通图,则有向图是弱连通图。
- 连通分量:无向图 G的一个极大连通子图称为 G的一个连通分量(或连通分支)。连通图只有一个连通分量,即其自身;非连通的无向图有多个连通分量。
生成树
## 图的存储方式
1.邻接矩阵:就是二维数组,可以快速定位到指定的边,但是如果是稀疏的图,会比较浪费空间。
2.邻接表:适合稀疏图,节省空间,存储方式决定了它只能表示某个顶点的入度或者出度,不能快速定位到某一条边。
3.十字链表
4.邻接多重表
5.边集数组
极小连通子图
有向树
出度(Outdegree)
路径(path)
回路(环)
简单路径
简单回路(简单环)
连通
连通图(Connected graph)、
连通分量(Connected Component)、
强连通图、
强连通分量(有向图中的极大强连通子图)、
生成树、
极小连通子图、
有向树。
无向图的邻接表存储-深度优先搜索
package com.wangpos.datastructure.graph;
import android.util.Log;
/**
* Created by qiyue on 2018/1/8.
* <p>
* 无向图的邻接表存储方式
*/
public class UndirectedGraph {
/**
* 整个数组表示一个图,也就是多个头结点信息结合构成
*/
private VNode mVNodeArrays[];
/**
* 图的大小
*/
private int size;
/**
* 创建图(用已提供的矩阵)
* <p>
* vexs -- 顶点数组
* edges -- 边数组
*/
public UndirectedGraph(String[] vexs, String[][] edges) {
size = vexs.length;
mVNodeArrays = new VNode[size];
//初始化定点信息
for (int i = 0; i < size; i++) {
mVNodeArrays[i] = new VNode();
mVNodeArrays[i].data = vexs[i];
mVNodeArrays[i].firstEdge = null;
}
//将顶点和边链接一起
for (int j = 0; j < size; j++) {
String start = edges[j][0];
String end = edges[j][1];
int startPosition = getPosition(start);
int endPosition = getPosition(end);
ENode eNode = new ENode(endPosition);
if (mVNodeArrays[startPosition].firstEdge == null) {
mVNodeArrays[startPosition].firstEdge = eNode;
} else {
linkLast(mVNodeArrays[startPosition].firstEdge, eNode);
}
}
}
private int getPosition(String start) {
int target = -1;
for (int i = 0; i < size; i++) {
String data = mVNodeArrays[i].data;
if (data.equals(start)) {
target = i;
break;
}
}
return target;
}
private void linkLast(ENode list, ENode node) {
ENode p = list;
while (p.nextEdge != null)
p = p.nextEdge;
p.nextEdge = node;
}
/**
* 头结点信息
*/
private class VNode {
public String data;//直接用String也可以,就是浪费了一点空间
public ENode firstEdge;
}
/**
* 边,通过该边对象可以知道该末尾端点和下一条边
*/
private class ENode {
public int ivex; //该边对应结束点的位置
public ENode nextEdge; // 这里是为了链接下一边
public ENode(int ivex) {
this.ivex = ivex;
}
}
/**
* I/tu ( 7206): 0A>2(C)>3(D)>5(F)
I/tu ( 7206): 1B
I/tu ( 7206): 2C>1(B)>3(D)
I/tu ( 7206): 3D
I/tu ( 7206): 4E
I/tu ( 7206): 5F>6(G)
I/tu ( 7206): 6G>4(E)
*/
public void print() {
System.out.printf("List Graph:\n");
for (int i = 0; i < mVNodeArrays.length; i++) {
// Log.i("tu", "顶点>>"+i + "" + mVNodeArrays[i].data);
StringBuilder sb = new StringBuilder();
sb.append("" + i + "" + mVNodeArrays[i].data);
ENode node = mVNodeArrays[i].firstEdge;
while (node != null) {
sb.append(">" + node.ivex + "(" + mVNodeArrays[node.ivex].data + ")");
node = node.nextEdge;
}
Log.i("tu", sb.toString());
// System.out.printf("\n");
}
}
/**
* 深度优先搜索遍历图 A C B D F G E
*/
public void DFS() {
boolean[] visited = new boolean[mVNodeArrays.length]; // 顶点访问标记
// 初始化所有顶点都没有被访问
for (int i = 0; i < mVNodeArrays.length; i++)
visited[i] = false;
Log.i("tu", "DFS:");
for (int i = 0; i < mVNodeArrays.length; i++) {
if (!visited[i])
DFS(i, visited);
}
Log.i("tu", "\n");
}
/**
* 深度优先搜索
* @param i
* @param visited
*/
private void DFS(int i, boolean[] visited) {
ENode node;
visited[i] = true;
// System.out.printf("%c ", mVNodeArrays[i].data);
Log.i("tu", mVNodeArrays[i].data);
node = mVNodeArrays[i].firstEdge;
while (node != null) {
if (!visited[node.ivex])
DFS(node.ivex, visited);
node = node.nextEdge;
}
}
/**
* 广度优先搜索 A C D F B G E
*/
public void BFS() {
boolean[] visited = new boolean[mVNodeArrays.length]; // 顶点访问标记
// 初始化所有顶点都没有被访问
for (int i = 0; i < mVNodeArrays.length; i++) {
visited[i] = false;
}
Log.i("tu", "BFS");
int head = 0;
int rear = 0;
int[] queue = new int[mVNodeArrays.length];
for (int i = 0; i < mVNodeArrays.length; i++) {
if (!visited[i]) {
visited[i] = true;
Log.i("tu", "y" + mVNodeArrays[i].data);
//入列
queue[rear] = i;
rear++;
}
//rear之前的都是被访问的点,通过header 去访问每个点下一层,访问的点通过rear位置加入队列
while (head != rear) {
int j = queue[head];
ENode node = mVNodeArrays[j].firstEdge;
// Log.i("tu", "node--"+mVNodeArrays[j].data +"node="+node);
// 开始遍历j的所有边,并且入队列 start
while (node != null) {
int k = node.ivex;
// Log.i("tu", "node--"+mVNodeArrays[j].data +"k="+k+"visited[k]= "+visited[k]);
if (!visited[k]) {
visited[k] = true;
Log.i("tu", "" + mVNodeArrays[k].data);
queue[rear] = k;
rear++;
}
node = node.nextEdge;
}
// 开始遍历j的所有边,end
// Log.i("tu", "》》》》" );
//出队列,
head++;
}
}
}
}
无向图的邻接表存储-广度优先搜索
package com.wangpos.datastructure.graph;
import android.util.Log;
/**
* Created by qiyue on 2018/1/8.
* <p>
* 无向图的邻接表存储方式
*/
public class UndirectedGraph {
/**
* 整个数组表示一个图,也就是多个头结点信息结合构成
*/
private VNode mVNodeArrays[];
/**
* 图的大小
*/
private int size;
/**
* 创建图(用已提供的矩阵)
* <p>
* vexs -- 顶点数组
* edges -- 边数组
*/
public UndirectedGraph(String[] vexs, String[][] edges) {
size = vexs.length;
mVNodeArrays = new VNode[size];
//初始化定点信息
for (int i = 0; i < size; i++) {
mVNodeArrays[i] = new VNode();
mVNodeArrays[i].data = vexs[i];
mVNodeArrays[i].firstEdge = null;
}
//将顶点和边链接一起
for (int j = 0; j < size; j++) {
String start = edges[j][0];
String end = edges[j][1];
int startPosition = getPosition(start);
int endPosition = getPosition(end);
ENode eNode = new ENode(endPosition);
if (mVNodeArrays[startPosition].firstEdge == null) {
mVNodeArrays[startPosition].firstEdge = eNode;
} else {
linkLast(mVNodeArrays[startPosition].firstEdge, eNode);
}
}
}
private int getPosition(String start) {
int target = -1;
for (int i = 0; i < size; i++) {
String data = mVNodeArrays[i].data;
if (data.equals(start)) {
target = i;
break;
}
}
return target;
}
private void linkLast(ENode list, ENode node) {
ENode p = list;
while (p.nextEdge != null)
p = p.nextEdge;
p.nextEdge = node;
}
/**
* 头结点信息
*/
private class VNode {
public String data;//直接用String也可以,就是浪费了一点空间
public ENode firstEdge;
}
/**
* 边,通过该边对象可以知道该末尾端点和下一条边
*/
private class ENode {
public int ivex; //该边对应结束点的位置
public ENode nextEdge; // 这里是为了链接下一边
public ENode(int ivex) {
this.ivex = ivex;
}
}
/**
* I/tu ( 7206): 0A>2(C)>3(D)>5(F)
I/tu ( 7206): 1B
I/tu ( 7206): 2C>1(B)>3(D)
I/tu ( 7206): 3D
I/tu ( 7206): 4E
I/tu ( 7206): 5F>6(G)
I/tu ( 7206): 6G>4(E)
*/
public void print() {
System.out.printf("List Graph:\n");
for (int i = 0; i < mVNodeArrays.length; i++) {
// Log.i("tu", "顶点>>"+i + "" + mVNodeArrays[i].data);
StringBuilder sb = new StringBuilder();
sb.append("" + i + "" + mVNodeArrays[i].data);
ENode node = mVNodeArrays[i].firstEdge;
while (node != null) {
sb.append(">" + node.ivex + "(" + mVNodeArrays[node.ivex].data + ")");
node = node.nextEdge;
}
Log.i("tu", sb.toString());
// System.out.printf("\n");
}
}
/**
* 深度优先搜索遍历图 A C B D F G E
*/
public void DFS() {
boolean[] visited = new boolean[mVNodeArrays.length]; // 顶点访问标记
// 初始化所有顶点都没有被访问
for (int i = 0; i < mVNodeArrays.length; i++)
visited[i] = false;
Log.i("tu", "DFS:");
for (int i = 0; i < mVNodeArrays.length; i++) {
if (!visited[i])
DFS(i, visited);
}
Log.i("tu", "\n");
}
/**
* 深度优先搜索
* @param i
* @param visited
*/
private void DFS(int i, boolean[] visited) {
ENode node;
visited[i] = true;
// System.out.printf("%c ", mVNodeArrays[i].data);
Log.i("tu", mVNodeArrays[i].data);
node = mVNodeArrays[i].firstEdge;
while (node != null) {
if (!visited[node.ivex])
DFS(node.ivex, visited);
node = node.nextEdge;
}
}
/**
* 广度优先搜索 A C D F B G E
*/
public void BFS() {
boolean[] visited = new boolean[mVNodeArrays.length]; // 顶点访问标记
// 初始化所有顶点都没有被访问
for (int i = 0; i < mVNodeArrays.length; i++) {
visited[i] = false;
}
Log.i("tu", "BFS");
int head = 0;
int rear = 0;
int[] queue = new int[mVNodeArrays.length];
for (int i = 0; i < mVNodeArrays.length; i++) {
if (!visited[i]) {
visited[i] = true;
Log.i("tu", "y" + mVNodeArrays[i].data);
//入列
queue[rear] = i;
rear++;
}
//rear之前的都是被访问的点,通过header 去访问每个点下一层,访问的点通过rear位置加入队列
while (head != rear) {
int j = queue[head];
ENode node = mVNodeArrays[j].firstEdge;
// Log.i("tu", "node--"+mVNodeArrays[j].data +"node="+node);
// 开始遍历j的所有边,并且入队列 start
while (node != null) {
int k = node.ivex;
// Log.i("tu", "node--"+mVNodeArrays[j].data +"k="+k+"visited[k]= "+visited[k]);
if (!visited[k]) {
visited[k] = true;
Log.i("tu", "" + mVNodeArrays[k].data);
queue[rear] = k;
rear++;
}
node = node.nextEdge;
}
// 开始遍历j的所有边,end
// Log.i("tu", "》》》》" );
//出队列,
head++;
}
}
}
}
无向图的邻接矩阵存储-深度优先搜索
package com.wangpos.datastructure.graph;
import android.util.Log;
/**
* Created by qiyue on 2018/1/9.
*
* 无向图的邻接矩阵存储方式
*/
public class UndirectedGraphMatrix {
public String[] getmVexs() {
return mVexs;
}
public void setmVexs(String[] mVexs) {
this.mVexs = mVexs;
}
public int[][] getmMatrix() {
return mMatrix;
}
public void setmMatrix(int[][] mMatrix) {
this.mMatrix = mMatrix;
}
private String[] mVexs; // 顶点集合
private int[][] mMatrix; // 邻接矩阵
public UndirectedGraphMatrix(String []vexs,String [][]edges){
// 初始化"顶点数"和"边数"
int vlen = vexs.length;
int elen = edges.length;
// 初始化"顶点"
mVexs = new String[vlen];
for (int i = 0; i < mVexs.length; i++)
mVexs[i] = vexs[i];
// 初始化"边"
mMatrix = new int[vlen][vlen];
for (int i = 0; i < elen; i++) {
// 读取边的起始顶点和结束顶点
int p1 = getPosition(edges[i][0]);
int p2 = getPosition(edges[i][1]);
/**
* 无向图是这样的
*/
mMatrix[p1][p2] = 1;
mMatrix[p2][p1] = 1;
}
}
private int getPosition(String s) {
for(int i=0; i<mVexs.length; i++) {
if (mVexs[i].equals(s)) {
return i;
}
}
return -1;
}
/*
* 返回顶点v的第一个邻接顶点的索引,失败则返回-1
*/
private int firstVertex(int v) {
if (v<0 || v>(mVexs.length-1))
return -1;
for (int i = 0; i < mVexs.length; i++)
if (mMatrix[v][i] == 1)
return i;
return -1;
}
/*
* 返回顶点v相对于w的下一个邻接顶点的索引,失败则返回-1
*/
private int nextVertex(int v, int w) {
if (v<0 || v>(mVexs.length-1) || w<0 || w>(mVexs.length-1))
return -1;
for (int i = w + 1; i < mVexs.length; i++)
if (mMatrix[v][i] == 1)
return i;
return -1;
}
/*
* 深度优先搜索遍历图的递归实现
*/
private void DFS(int i, boolean[] visited) {
visited[i] = true;
Log.i("tu",mVexs[i]);
// 遍历该顶点的所有邻接顶点。若是没有访问过,那么继续往下走
for (int w = firstVertex(i); w >= 0; w = nextVertex(i, w)) {
if (!visited[w])
DFS(w, visited);
}
}
/*
* 深度优先搜索遍历图
*/
public void DFS() {
boolean[] visited = new boolean[mVexs.length]; // 顶点访问标记
// 初始化所有顶点都没有被访问
for (int i = 0; i < mVexs.length; i++)
visited[i] = false;
Log.i("tu","DFS: ");
for (int i = 0; i < mVexs.length; i++) {
if (!visited[i])
DFS(i, visited);
}
Log.i("tu","");
}
/*
* 广度优先搜索(类似于树的层次遍历)
*/
public void BFS() {
int head = 0;
int rear = 0;
int[] queue = new int[mVexs.length]; // 辅组队列
boolean[] visited = new boolean[mVexs.length]; // 顶点访问标记
for (int i = 0; i < mVexs.length; i++)
visited[i] = false;
Log.i("tu","BFS: ");
for (int i = 0; i < mVexs.length; i++) {
if (!visited[i]) {
visited[i] = true;
Log.i("tu", mVexs[i]);
queue[rear++] = i; // 入队列
}
while (head != rear) {
int j = queue[head++]; // 出队列
for (int k = firstVertex(j); k >= 0; k = nextVertex(j, k)) { //k是为访问的邻接顶点
if (!visited[k]) {
visited[k] = true;
Log.i("tu", mVexs[k]);
queue[rear++] = k;
}
}
}
}
Log.i("tu","");
}
}
无向图的邻接矩阵存储-广度优先搜索
package com.wangpos.datastructure.graph;
import android.util.Log;
/**
* Created by qiyue on 2018/1/9.
*
* 无向图的邻接矩阵存储方式
*/
public class UndirectedGraphMatrix {
public String[] getmVexs() {
return mVexs;
}
public void setmVexs(String[] mVexs) {
this.mVexs = mVexs;
}
public int[][] getmMatrix() {
return mMatrix;
}
public void setmMatrix(int[][] mMatrix) {
this.mMatrix = mMatrix;
}
private String[] mVexs; // 顶点集合
private int[][] mMatrix; // 邻接矩阵
public UndirectedGraphMatrix(String []vexs,String [][]edges){
// 初始化"顶点数"和"边数"
int vlen = vexs.length;
int elen = edges.length;
// 初始化"顶点"
mVexs = new String[vlen];
for (int i = 0; i < mVexs.length; i++)
mVexs[i] = vexs[i];
// 初始化"边"
mMatrix = new int[vlen][vlen];
for (int i = 0; i < elen; i++) {
// 读取边的起始顶点和结束顶点
int p1 = getPosition(edges[i][0]);
int p2 = getPosition(edges[i][1]);
/**
* 无向图是这样的
*/
mMatrix[p1][p2] = 1;
mMatrix[p2][p1] = 1;
}
}
private int getPosition(String s) {
for(int i=0; i<mVexs.length; i++) {
if (mVexs[i].equals(s)) {
return i;
}
}
return -1;
}
/*
* 返回顶点v的第一个邻接顶点的索引,失败则返回-1
*/
private int firstVertex(int v) {
if (v<0 || v>(mVexs.length-1))
return -1;
for (int i = 0; i < mVexs.length; i++)
if (mMatrix[v][i] == 1)
return i;
return -1;
}
/*
* 返回顶点v相对于w的下一个邻接顶点的索引,失败则返回-1
*/
private int nextVertex(int v, int w) {
if (v<0 || v>(mVexs.length-1) || w<0 || w>(mVexs.length-1))
return -1;
for (int i = w + 1; i < mVexs.length; i++)
if (mMatrix[v][i] == 1)
return i;
return -1;
}
/*
* 深度优先搜索遍历图的递归实现
*/
private void DFS(int i, boolean[] visited) {
visited[i] = true;
Log.i("tu",mVexs[i]);
// 遍历该顶点的所有邻接顶点。若是没有访问过,那么继续往下走
for (int w = firstVertex(i); w >= 0; w = nextVertex(i, w)) {
if (!visited[w])
DFS(w, visited);
}
}
/*
* 深度优先搜索遍历图
*/
public void DFS() {
boolean[] visited = new boolean[mVexs.length]; // 顶点访问标记
// 初始化所有顶点都没有被访问
for (int i = 0; i < mVexs.length; i++)
visited[i] = false;
Log.i("tu","DFS: ");
for (int i = 0; i < mVexs.length; i++) {
if (!visited[i])
DFS(i, visited);
}
Log.i("tu","");
}
/*
* 广度优先搜索(类似于树的层次遍历)
*/
public void BFS() {
int head = 0;
int rear = 0;
int[] queue = new int[mVexs.length]; // 辅组队列
boolean[] visited = new boolean[mVexs.length]; // 顶点访问标记
for (int i = 0; i < mVexs.length; i++)
visited[i] = false;
Log.i("tu","BFS: ");
for (int i = 0; i < mVexs.length; i++) {
if (!visited[i]) {
visited[i] = true;
Log.i("tu", mVexs[i]);
queue[rear++] = i; // 入队列
}
while (head != rear) {
int j = queue[head++]; // 出队列
for (int k = firstVertex(j); k >= 0; k = nextVertex(j, k)) { //k是为访问的邻接顶点
if (!visited[k]) {
visited[k] = true;
Log.i("tu", mVexs[k]);
queue[rear++] = k;
}
}
}
}
Log.i("tu","");
}
}
拓扑排序-邻接矩阵存储-Kahn算法
package com.wangpos.datastructure.graph;
import com.wangpos.datastructure.R;
import com.wangpos.datastructure.core.BaseActivity;
import com.wangpos.datastructure.core.CodeBean;
import java.util.List;
/**
* Created by qiyue on 2018/1/9.
*/
public class TopologicalOrderActivity extends BaseActivity {
@Override
protected void initData() {
addImage("发现V6和v1是没有前驱的,所以我们就随机选去一个输出,我们先输出V6,删除和V6有关的边,得到如下图", R.drawable.t2);
addImage("然后,我们继续寻找没有前驱的顶点,发现V1没有前驱,所以输出V1,删除和V1有关的边,得到下图的结果", R.drawable.t3);
addImage("然后,我们又发现V4和V3都是没有前驱的,那么我们就随机选取一个顶点输出(具体看你实现的算法和图存储结构),我们输出V4,得到如下图结果: ", R.drawable.t4);
addImage("然后,我们输出没有前驱的顶点V3,得到如下结果:", R.drawable.t5);
addItem(new CodeBean("拓扑排序结果","v6–>v1—->v4—>v3—>v5—>v2"));
String[] vexs = {"V1", "V2", "V3", "V4", "V5", "V6"};
String[][] edges = new String[][]{
{"V1", "V2"},
{"V1", "V4"},
{"V1", "V3"},
{"V3", "V2"},
{"V3", "V5"},
{"V4", "V5"},
{"V6", "V4"},
{"V6", "V5"}
};
//创建有向图
DirectedGraphMatrix graphMatrix = new DirectedGraphMatrix(vexs,edges);
// graphMatrix.printPointDegree();
// graphMatrix.toplogicSort();
graphMatrix.toplogicSortByDFS();
// List<Integer> result = graphMatrix.getDfsResult();
addItem(new CodeBean("深度优先搜搜拓扑排序",sortByDFSCode));
}
@Override
protected String getTextData() {
return null;
}
@Override
protected int getImageData() {
return R.drawable.t1;
}
@Override
protected String getResultData() {
return " 1.拓扑排序介绍,对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。 \n" +
"\n" +
" 2.举例子,当我们想选修无人驾驶研发课程的时候,我们需要学习机器学习课程,同时学习这个课程我们还要学习算法,并且需要学习一门语言,这种决定了那些课程必须先执行,如果课程很多的话,我们可以来做一个拓扑排序,来决定要先学那些课程\n" +
"\n" +
" 3.拓扑排序前提,是一个有向无环图。想想如果我们的选课的时候出现了环路,相互依赖了,那就没办法进行了\n" +
"\n" +
" 4.拓扑排序步骤:\t\n" +
" A.\t在有向图中选一个没有前驱的顶点并且输出\n" +
" B. 从图中删除该顶点和所有以它为尾的弧(白话就是:删除所有和它有关的边)\n" +
" C. 重复上述两步,直至所有顶点输出,或者当前图中不存在无前驱的顶点为止,后者代表我们的有向图是有环的,因此,也可以通过拓扑排序来判断一个图是否有环。" +
"" +
"5.拓扑排序的结果不一定是唯一的,有可能存在多个入度为0的点";
}
@Override
protected String getTimeData() {
return null;
}
@Override
protected String getSpaceTimeData() {
return null;
}
@Override
protected String getWendingXingData() {
return null;
}
@Override
protected String getSummaryData() {
return "\n" +
"拓扑排序算法\n" +
"\n" +
"1.Kahn 算法 \n" +
" 每次从该集合中取出(没有特殊的取出规则,随机取出也行,使用队列/栈也行,下同)一个顶点,将该顶点放入保存结果的List中。\n" +
"紧接着循环遍历由该顶点引出的所有边,从图中移除这条边,同时获取该边的另外一个顶点,如果该顶点的入度在减去本条边之后为0,那么也将这个顶点放到入度为0的集合中。然后继续从集合中取出一个顶点\n" +
" \n" +
"注意:当集合为空之后,检查图中是否还存在任何边,如果存在的话,说明图中至少存在一条环路。不存在的话则返回结果List,此List中的顺序就是对图进行拓扑排序的结果。\n" +
"\n" +
"2.深度优先搜索算法 \n" +
"\n" +
" DFS的实现更加简单直观,使用递归实现。利用DFS实现拓扑排序,实际上只需要添加一行代码,即上面伪码中的最后一行:add n to L。\n" +
"需要注意的是,将顶点添加到结果List中的时机是在visit方法即将退出之时。\n" +
"这个算法的实现非常简单,但是要理解的话就相对复杂一点。\n" +
"关键在于为什么在visit方法的最后将该顶点添加到一个集合中,就能保证这个集合就是拓扑排序的结果呢?\n" +
"因为添加顶点到集合中的时机是在dfs方法即将退出之时,而dfs方法本身是个递归方法,只要当前顶点还存在边指向其它任何顶点,它就会递归调用dfs方法,而不会退出。因此,退出dfs方法,意味着当前顶点没有指向其它顶点的边了,即当前顶点是一条路径上的最后一个顶点。\n" +
" \n" +
"下面简单证明一下它的正确性:\n" +
"考虑任意的边v->w,当调用dfs(v)的时候,有如下三种情况:\n" +
"1. dfs(w)还没有被调用,即w还没有被mark,此时会调用dfs(w),然后当dfs(w)返回之后,dfs(v)才会返回\n" +
"2. dfs(w)已经被调用并返回了,即w已经被mark\n" +
"3. dfs(w)已经被调用但是在此时调用dfs(v)的时候还未返回\n" +
"需要注意的是,以上第三种情况在拓扑排序的场景下是不可能发生的,因为如果情况3是合法的话,就表示存在一条由w到v的路径。而现在我们的前提条件是由v到w有一条边,这就导致我们的图中存在环路,从而该图就不是一个有向无环图(DAG),而我们已经知道,非有向无环图是不能被拓扑排序的。\n" +
" \n" +
"那么考虑前两种情况,无论是情况1还是情况2,w都会先于v被添加到结果列表中。所以边v->w总是由结果集中后出现的顶点指向先出现的顶点。为了让结果更自然一些,可以使用栈来作为存储最终结果的数据结构,从而能够保证边v->w总是由结果集中先出现的顶点指向后出现的顶点\n";
}
public static String sortByKahnCode = "" +
"public void toplogicSort() {\n" +
" int header = 0;\n" +
" int result[] = new int[mVexs.length];\n" +
" for (int i = 0; i < mVexs.length; i++) {\n" +
" result[i] = -1;\n" +
" }\n" +
"\n" +
" for (int i = 0; ; i++) {\n" +
" if (inDegrees[i] == 0) {\n" +
"\n" +
" boolean isVisit = false;\n" +
" for (int m = 0; m < header; m++) {\n" +
" if (result[m] == i) {\n" +
" isVisit = true;\n" +
" }\n" +
" }\n" +
" if (!isVisit) {\n" +
" result[header] = i;\n" +
" deletePositin(i);\n" +
" header++;\n" +
" if (header == inDegrees.length) {\n" +
" break;\n" +
" }\n" +
"\n" +
" }\n" +
" if (i == 5) {\n" +
" i = 0;\n" +
" }\n" +
" }\n" +
" }\n" +
"\n" +
" /***\n" +
" * 输出\n" +
" */\n" +
"\n" +
" for (int i = 0; i < header; i++) {\n" +
" Log.i(\"tu\", mVexs[result[i]]);\n" +
" }\n" +
"\n" +
" }";
public static String sortByDFSCode = "\n" +
" public void toplogicSortByDFS(){\n" +
"\n" +
"\n" +
" boolean[] visited = new boolean[mVexs.length]; // 顶点访问标记\n" +
"\n" +
" // 初始化所有顶点都没有被访问\n" +
" for (int i = 0; i < mVexs.length; i++)\n" +
" visited[i] = false;\n" +
"\n" +
" Log.i(\"tu\",\"DFS: \");\n" +
" for (int i = 0; i < mVexs.length; i++) {\n" +
" if (!visited[i]) {\n" +
" DFS(i, visited);\n" +
" }\n" +
" }\n" +
"\n" +
" for (int i=dfsResult.size()-1;i>=0;i--){\n" +
" Log.i(\"topo\",mVexs[dfsResult.get(i).intValue()]);\n" +
" }\n" +
" }\n" +
"\n" +
"\n" +
"\n" +
" /*\n" +
" * 深度优先搜索遍历图的递归实现\n" +
" */\n" +
" private void DFS(int i, boolean[] visited) {\n" +
"\n" +
" visited[i] = true;\n" +
" Log.i(\"tu\",mVexs[i]);\n" +
" // 遍历该顶点的所有邻接顶点。若是没有访问过,那么继续往下走\n" +
" for (int w = firstVertex(i); w >= 0; w = nextVertex(i, w)) {\n" +
" if (!visited[w]) {\n" +
" DFS(w, visited);\n" +
"\n" +
" }\n" +
" }\n" +
" dfsResult.add(i);\n" +
" }";
}
回复关键字:
1、回复 “10” 查看 最有价值的10个spring boot开源项目
2、回复 “国旗” 获取国旗头像教程**
3、回复 “Ubuntu” 获取****100 个最佳 Ubuntu 应用 和 linux神器
4、回复 “idea” 获取**最新idea破解教程 和 装逼神奇
5、回复 “ssh” 获取史上最好的 ssh工具 支持mac
6、回复 “代金券” 免费获取腾讯云和阿里云代金券
推荐阅读:
Java 开发人员常用的服务配置(Nginx、Tomcat、JVM、Mysql、Redis)
面试官问我:一个 TCP 连接可以发多少个 HTTP 请求?我竟然回答不上来..
我的官网
在这里插入图片描述
我的CSDN地址http://blog.csdn.net/chenjianandiyi
我的简书地址http://www.jianshu.com/u/9b5d1921ce34
我的githubhttps://github.com/javanan
我的码云地址https://gitee.com/jamen/
阿里云优惠券与阿里云上云教程<http://aliyun.guan2ye.com/>
** 个人微信公众号: dou_zhe_wan **
欢迎关注
在这里插入图片描述
免责声明:
1.本公众号所转载文章均来自公开网络。
2.如果出处标注有误或侵犯到原著作者权益,请联系删除。
3.转载本公众号中的文章请注明原文链接和作者,否则产生的任何版权纠纷均与本公众号无关。
网友评论