https://github.com/javanan/DataStructurejava
时间复杂度介绍
空间复杂度介绍
递归算法与非递归算法区别和转换
折半查找/二分查找算法
链表实现
反转一个链表
直接插入排序
快速排序
选择排序
冒泡排序
线程与锁详解
二叉树的遍历
二叉排序树
图的详解
图的邻接表存储构成图
无向图的邻接表存储-深度优先搜索
无向图的邻接表存储-广度优先搜索
无向图的邻接矩阵存储-深度优先搜索
无向图的邻接矩阵存储-广度优先搜索
有向图的建立
拓扑排序-邻接矩阵存储-Kahn算法
拓扑排序-邻接矩阵存储-深度优先搜索算法
最短路径算法之Dijkstra算法(狄克斯特拉算法
ArrayList实现原理
LinkList双向实现
堆排序
归并排序
希尔排序
八大排序总结
计数排序
同时找出最大值和最小值最优算法
快速查找法,查找第k个最大的数
10亿数据查找前100个
散列表(哈希表)
求最大不重复子串
死锁
两个线程交替输出1010node
关注我,一个仍存梦想的屌丝程序员,天天为你分享高质量编程博客。python
阿里云优惠券与阿里云上云教程<http://aliyun.guan2ye.com/>linux
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",""); } }
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" + " }"; }
package com.wangpos.datastructure.sort; import android.app.ActionBar; import android.util.Log; import com.wangpos.datastructure.R; import com.wangpos.datastructure.core.BaseActivity; import com.wangpos.datastructure.core.CodeBean; import java.util.Arrays; /** * Created by qiyue on 2017/11/22. */ public class HeapSortActivity extends BaseActivity { int arr[] = { 1, 3, 4, 5, 2, 6, 9, 7, 8, 0 }; @Override protected void initData() { setTitleText("堆排序"); addItem(new CodeBean("堆的构建建和排序",heapSortCode)); addItem(new CodeBean("堆的递归算法",heapAdjustRecursionCode)); addItem(new CodeBean("堆的非递归算法",heapAdjustIteration)); addItem(new CodeBean("堆的非递归算法优化",heapAdjustIterationGood)); addImage("第一次比较,找到最后一个节点的父节,根结点编号为0开始,第i个元素,经过公式(i-1)/2获得他的父亲节点", R.drawable.dui1); addImage("第二次比较", R.drawable.dui2); addImage("第三次比较", R.drawable.dui3); addImage("第四次比较", R.drawable.dui4); addImage("第五次比较", R.drawable.dui5); addImage("一个堆结构数据", R.drawable.dui6); // Log.i("info","排序后="+Arrays.toString(arr)); } @Override protected String getTextData() { return Arrays.toString(arr); } @Override protected int getImageData() { return 0; } @Override protected String getResultData() { return Arrays.toString(heapSort(arr)); } @Override protected String getTimeData() { return "O(nlogn)"; } @Override protected String getSpaceTimeData() { return "O(1)"; } @Override protected String getWendingXingData() { return "不稳定"; } @Override protected String getSummaryData() { return " 堆排序,这里的堆不是java中的堆,是一种数据结构,简单的理解就是一个彻底二叉树\n" + " 堆的定义\n" + "n个元素的序列{k1,k2,…,kn}当且仅当知足下列关系之一时,称之为堆。\n" + "\n" + " 情形1:ki <= k2i 且ki <= k2i+1 (最小化堆或小顶堆)\n" + "\n" + " 情形2:ki >= k2i 且ki >= k2i+1 (最大化堆或大顶堆)\n" + "\n" + "若将和此序列对应的一维数组(即以一维数组做此序列的存储结构)当作是一个彻底二叉树," + "则堆的含义代表,彻底二叉树中全部非终端结点的值均不大于(或不小于)其左、右孩子结点的值。" + "\n" + "\n" + "" + "堆的存储\n" + "" + "<<通常用数组来表示堆,若根结点存在序号0处, i结点的父结点下标就为(i-1)/2。i结点的左右子结点下标分别为2*i+1和2*i+2。>>\n" + "" + ""; } /** * * 通常用数组来表示堆,若根结点存在序号0处, i结点的父结点下标就为(i-1)/2。i结点的左右子结点下标分别为2*i+1和2*i+2。 * * * 调整堆,也能够叫构建堆,准确的说是构建 * * 交换都是最后一步, while循环内只是覆盖,上移,每次找出自节点最大最大值给parent赋值,自身先不变, * 下次循环自身变为parent,找出孩子中最大值赋值给自身 * * 循环终止条件 是给定一个parent点,大于其孩子 小于 parent 就会终止, * * @param array 数组 1, 3, 4, 5, 2, 6, 9, 7, 8, 0 * @param parent * @param length */ public void HeapAdjust(int[] array, int parent, int length) { int temp = array[parent]; // temp保存当前父节点 第一次始终是0 int child = 2 * parent + 1; // 先得到左孩子 while (child < length) { /** * 两个孩子比较大小 */ // 若是有右孩子结点,而且右孩子结点的值大于左孩子结点,则选取右孩子结点 if (child + 1 < length && array[child] < array[child + 1]) { child++; } /** * 孩子中最大的和父亲比较大小,若是父亲大,跳出循环,不然把最大值和父亲交换 */ // 若是父结点的值已经大于孩子结点的值,则直接结束 if (temp >= array[child]) { break; } // 把孩子结点的值赋给父结点 array[parent] = array[child]; // 选取孩子结点的左孩子结点,继续向下筛选 /** * 再采用从后构建堆的时候,这里就会没用 */ parent = child; child = 2 * child + 1; Log.i("info","temp="+temp +"HeapAdjust="+Arrays.toString(array)); } /** * 将本次父节点赋值给最后一个parent ,parent多是本身 */ array[parent] = temp; } private static final String heapSortCode = "/**\n" + " * 创建堆的过程必须从第一个叶子节点的父节点开始构建,原理就是,咱们必需要创建小的堆,逐渐扩大,\n" + " *\n" + " * 创建堆算法解析\n" + " *\n" + " * 第一趟按要求,从倒数第二层开始,只有两层,从第一个叶子节点的父节点开始,判断是否孩子中最大的,若是不是,则和孩子中最大值交换\n" + " * 第二趟指针前移动,若是仍是在倒数第二层和第一趟算法同样\n" + " * 通过几回指针移动后,到了倒数第三层,此时是三层结构,也判断是否孩子中最大的,若是不是,把最大值拿过来,但最小值不要着急给出去,\n" + " * 有可能这个值小的离谱,因此继续顺着这个最大值的分支,找他的孩子比较,若是验证已经大于孩子就中止,否者继续向下层比较\n" + " *\n" + " * 第一趟的算法能够写成这样:\n" + " *\n" + " * int temp = array[parent];\n" + " * int child = 2 * parent + 1;\n" + " * if (child + 1 < length && array[child] < array[child + 1]) {\n" + " child++;\n" + " }\n" + "\n" + " if (temp >= array[child]) {\n" + "\n" + " }else{\n" + " array[parent] = array[child];\n" + " }\n" + "\n" + " 再倒数三层以上的算法( 多出了向下查找判断):\n" + " int temp = array[parent];\n" + " int child = 2 * parent + 1;\n" + " if (child + 1 < length && array[child] < array[child + 1]) {\n" + " child++;\n" + " }\n" + "\n" + " if (temp >= array[child]) {\n" + "\n" + " }else{\n" + " array[parent] = array[child];\n" + " }\n" + "\n" + " //至关于递归\n" + " parent = child;\n" + " child = 2 * parent + 1;\n" + "\n" + " *\n" + " *\n" + " */\n" + " public int[] heapSort(int[] list) {\n" + "\n" + " // 循环创建初始堆\n" + " for (int i = (list.length-1) / 2; i >= 0; i--) {\n" + "// HeapAdjust(list, i, list.length);\n" + "// heapAdjustRecursion(list,i,list.length);\n" + "// heapAdjustIteration(list,i,list.length);\n" + " heapAdjustIterationGood(list,i,list.length);\n" + " }\n" + " //初始数据[9, 8, 6, 7, 2, 1, 4, 3, 5, 0]\n" + "\n" + " Log.i(\"info\", \"初始后堆\"+Arrays.toString(list));\n" + " /**\n" + " * 当前顶点和最后一个交换后,第一个数据有可能不符合堆结构,因此咱们要找到他的位置,\n" + " * 若是第一次比较如比孩子大,那就不须要更换,否者,将孩子中最大的换到本身位置,此时空缺位置检测放入3合不合适,\n" + " * 也就是继续和这个位子的孩子中最大值比较,若是大于,那就是这个位置\n" + " */\n" + " for (int i = list.length - 1; i > 0; i--) {\n" + " // 最后一个元素和第一元素进行交换\n" + " int temp = list[i];\n" + " list[i] = list[0];\n" + " list[0] = temp;\n" + " //第一次交换后的数据[0, 8, 6, 7, 2, 1, 4, 3, 5, 9]\n" + "// heapAdjustRecursion(list,0,i);\n" + "// heapAdjustIteration(list,0,i);\n" + " heapAdjustIterationGood(list,0,i);\n" + " //第一趟结果[8, 7, 6, 5, 2, 1, 4, 3, 0, 9]\n" + "\n" + "// Log.i(\"info\",\"第\"+(list.length - i)+\"趟list=\"+Arrays.toString(list));\n" + "\n" + " }\n" + " return list;\n" + "\n" + " }"; /** * 创建堆的过程必须从第一个叶子节点的父节点开始构建,原理就是,咱们必需要创建小的堆,逐渐扩大, * * 创建堆算法解析 * * 第一趟按要求,从倒数第二层开始,只有两层,从第一个叶子节点的父节点开始,判断是否孩子中最大的,若是不是,则和孩子中最大值交换 * 第二趟指针前移动,若是仍是在倒数第二层和第一趟算法同样 * 通过几回指针移动后,到了倒数第三层,此时是三层结构,也判断是否孩子中最大的,若是不是,把最大值拿过来,但最小值不要着急给出去, * 有可能这个值小的离谱,因此继续顺着这个最大值的分支,找他的孩子比较,若是验证已经大于孩子就中止,否者继续向下层比较 * * 第一趟的算法能够写成这样: * * int temp = array[parent]; * int child = 2 * parent + 1; * if (child + 1 < length && array[child] < array[child + 1]) { child++; } if (temp >= array[child]) { }else{ array[parent] = array[child]; } 再倒数三层以上的算法( 多出了向下查找判断): int temp = array[parent]; int child = 2 * parent + 1; if (child + 1 < length && array[child] < array[child + 1]) { child++; } if (temp >= array[child]) { }else{ array[parent] = array[child]; } //至关于递归 parent = child; child = 2 * parent + 1; * * */ public int[] heapSort(int[] list) { // 循环创建初始堆 for (int i = (list.length-1) / 2; i >= 0; i--) { // HeapAdjust(list, i, list.length); // heapAdjustRecursion(list,i,list.length); // heapAdjustIteration(list,i,list.length); heapAdjustIterationGood(list,i,list.length); } //初始数据[9, 8, 6, 7, 2, 1, 4, 3, 5, 0] Log.i("info", "初始后堆"+Arrays.toString(list)); /** * 当前顶点和最后一个交换后,第一个数据有可能不符合堆结构,因此咱们要找到他的位置, * 若是第一次比较如比孩子大,那就不须要更换,否者,将孩子中最大的换到本身位置,此时空缺位置检测放入3合不合适, * 也就是继续和这个位子的孩子中最大值比较,若是大于,那就是这个位置 */ for (int i = list.length - 1; i > 0; i--) { // 最后一个元素和第一元素进行交换 int temp = list[i]; list[i] = list[0]; list[0] = temp; //第一次交换后的数据[0, 8, 6, 7, 2, 1, 4, 3, 5, 9] // heapAdjustRecursion(list,0,i); // heapAdjustIteration(list,0,i); heapAdjustIterationGood(list,0,i); //第一趟结果[8, 7, 6, 5, 2, 1, 4, 3, 0, 9] // Log.i("info","第"+(list.length - i)+"趟list="+Arrays.toString(list)); } return list; } private static final String heapAdjustRecursionCode = "/**\n" + " * 递归方法\n" + " * 1.须要额外的方法栈空间\n" + " * 2.每次都须要交换\n" + " */\n" + " public void heapAdjustRecursion(int array[],int parent,int length){\n" + "\n" + " int temp = array[parent];\n" + " int child = 2 * parent + 1;\n" + "\n" + " if (child >=length){\n" + " Log.i(\"info\",\"child=\"+child+\"heapAdjustRecursion 结束\");\n" + " return;\n" + " }\n" + " if (child + 1 < length && array[child] < array[child + 1]) {\n" + " child++;\n" + " }\n" + "\n" + " if (temp >= array[child]) {\n" + "\n" + " }else{\n" + " array[parent] = array[child];\n" + " array[child] = temp;\n" + " }\n" + " parent = child;\n" + "\n" + " heapAdjustRecursion(array,parent,length);\n" + " }"; /** * 递归方法 * 1.须要额外的方法栈空间 * 2.每次都须要交换 */ public void heapAdjustRecursion(int array[],int parent,int length){ int temp = array[parent]; int child = 2 * parent + 1; if (child >=length){ Log.i("info","child="+child+"heapAdjustRecursion 结束"); return; } if (child + 1 < length && array[child] < array[child + 1]) { child++; } if (temp >= array[child]) { }else{ array[parent] = array[child]; array[child] = temp; } parent = child; heapAdjustRecursion(array,parent,length); } private static final String heapAdjustIteration = "/**\n" + " * 迭代算法(非递归算法)\n" + " * 每次都交换\n" + " */\n" + " public void heapAdjustIteration(int array[],int parent,int length){\n" + " int temp = array[parent];\n" + " int child = 2 *parent + 1;\n" + "\n" + " while(child<length){\n" + " /**选出最大child**/\n" + " if (child + 1<length && array[child]<array[child +1]){\n" + " child ++;\n" + " }\n" + "\n" + " if(temp>array[child]){\n" + " Log.i(\"info\",\"heapAdjustIteration结束\");\n" + " break;\n" + " }else{\n" + " array[parent] = array[child];\n" + " array[child] = temp;\n" + " parent = child;\n" + " child = 2 *parent +1;\n" + " }\n" + "\n" + " }\n" + "\n" + " }"; /** * 迭代算法(非递归算法) * 每次都交换 */ public void heapAdjustIteration(int array[],int parent,int length){ int temp = array[parent]; int child = 2 *parent + 1; while(child<length){ /**选出最大child**/ if (child + 1<length && array[child]<array[child +1]){ child ++; } if(temp>array[child]){ Log.i("info","heapAdjustIteration结束"); break; }else{ array[parent] = array[child]; array[child] = temp; parent = child; child = 2 *parent +1; } } } private static final String heapAdjustIterationGood = " /**\n" + " * 迭代算法(优化版)(非递归算法)\n" + " * 赋值不交换,最后一次再交换,减小通常的交换次数,也就是肯定最终位置(和选择排序同样)\n" + " */\n" + " public void heapAdjustIterationGood(int array[],int parent,int length){\n" + " int temp = array[parent];\n" + " int child = 2 *parent + 1;\n" + "\n" + " while(child<length){\n" + " /**选出最大child**/\n" + " if (child + 1<length && array[child]<array[child +1]){\n" + " child ++;\n" + " }\n" + "\n" + " if(temp>array[child]){\n" + " Log.i(\"info\",\"heapAdjustIteration结束\");\n" + " break;\n" + " }else{\n" + " array[parent] = array[child];\n" + "\n" + " parent = child;\n" + " child = 2 *parent +1;\n" + " }\n" + "\n" + " }\n" + " //指向最后一个,child 可能指向没有\n" + " array[parent] = temp;\n" + "// array[child] = temp;\n" + "\n" + " }"; /** * 迭代算法(优化版)(非递归算法) * 赋值不交换,最后一次再交换,减小通常的交换次数,也就是肯定最终位置(和选择排序同样) */ public void heapAdjustIterationGood(int array[],int parent,int length){ int temp = array[parent]; int child = 2 *parent + 1; while(child<length){ /**选出最大child**/ if (child + 1<length && array[child]<array[child +1]){ child ++; } if(temp>array[child]){ Log.i("info","heapAdjustIteration结束"); break; }else{ array[parent] = array[child]; parent = child; child = 2 *parent +1; } } //指向最后一个,child 可能指向没有 array[parent] = temp; // array[child] = temp; } }
package com.wangpos.datastructure.sort; import android.util.Log; import com.wangpos.datastructure.core.BaseActivity; import com.wangpos.datastructure.core.CodeBean; import java.util.Arrays; /** * Created by qiyue on 2017/11/23. */ public class MergeSortActivity extends BaseActivity { int arrays[] = {4,7,5,3,2,8,6,1}; @Override protected void initData() { setTitleText("归并排序"); addItem(new CodeBean("归并排序",mergeCode)); } @Override protected String getTextData() { return Arrays.toString(arrays); } @Override protected int getImageData() { return 0; } @Override protected String getResultData() { MergeSort(arrays); return Arrays.toString(arrays); } @Override protected String getTimeData() { return "对长度为n的文件,需进行 趟二路归并,每趟归并的时间为O(n),故其时间复杂度不管是在最好状况下仍是在最坏状况下均是O(nlgn)。"; } @Override protected String getSpaceTimeData() { return "须要一个辅助向量来暂存两有序子文件归并的结果,故其辅助空间复杂度为O(n),显然它不是就地排序。"; } @Override protected String getWendingXingData() { return "稳定"; } @Override protected String getSummaryData() { return "归并排序 \n" + " 归并排序 (merge sort) 是一类与插入排序、交换排序、选择排序不一样的另外一种排序方法。归并的含义是将两个或两个以上的有序表合并成一个新的有序表。归并排序有多路归并排序、两路归并排序 , 可用于内排序,也能够用于外排序。这里仅对内排序的两路归并方法进行讨论。 \n" + "1.两路归并排序算法思路\n" + " ①把 n 个记录当作 n 个长度为1的有序子表;\n" + " ②进行两两归并使记录关键字有序,获得 n/2 个长度为 2 的有序子表; \n" + " ③重复第②步直到全部记录归并成一个长度为 n 的有序表为止。"; } private static void MergeSort(int[] a) { Log.i("info","开始排序"); Sort(a, 0, a.length - 1); } private static void Sort(int[] a, int left, int right) { if(left>=right) return; int mid = (left + right) / 2; //二路归并排序里面有两个Sort,多路归并排序里面写多个Sort就能够了 Sort(a, left, mid); Sort(a, mid + 1, right); merge(a, left, mid, right); } /** * * 算法为三个方法 * * 归并排序是先拆分,按照2分或者3分等等,而后将最小不可分的 * * 而后须要一个辅助控件 * 和辅助的cIndex = left 以便后面拷贝 * 须要一个tmpIndex临时变量 * 和rightIndex做为右边判断 * * * @param a * @param left * @param mid * @param right */ private static void merge(int[] a, int left, int mid, int right) { int[] tmp = new int[a.length]; int rightIndex = mid + 1; int tmpIndex = left; int cIndex=left; /** * 左右逐个比较大小 */ while(left <=mid && rightIndex <= right) { if (a[left] <= a[rightIndex]) tmp[tmpIndex++] = a[left++]; else tmp[tmpIndex++] = a[rightIndex++]; } /** 比较过程有可能左边剩余,将左边剩余的归并 */ while (left <=mid) { tmp[tmpIndex++] = a[left++]; } /** 比较过程有可能右边剩余,将右边剩余的归并 */ while ( rightIndex <= right ) { tmp[tmpIndex++] = a[rightIndex++]; } // System.out.println("第"+(++number)+"趟排序:\t"); /** 从临时数组拷贝到原数组 **/ while(cIndex<=right){ a[cIndex]=tmp[cIndex]; //输出中间归并排序结果 System.out.print(a[cIndex]+"\t"); cIndex++; } System.out.println(); } private static final String mergeCode = " private static void MergeSort(int[] a) {\n" + " Log.i(\"info\",\"开始排序\");\n" + " Sort(a, 0, a.length - 1);\n" + " }\n" + "\n" + " private static void Sort(int[] a, int left, int right) {\n" + " if(left>=right)\n" + " return;\n" + "\n" + " int mid = (left + right) / 2;\n" + " //二路归并排序里面有两个Sort,多路归并排序里面写多个Sort就能够了\n" + " Sort(a, left, mid);\n" + " Sort(a, mid + 1, right);\n" + " merge(a, left, mid, right);\n" + "\n" + " }\n" + "\n" + "\n" + " private static void merge(int[] a, int left, int mid, int right) {\n" + "\n" + " int[] tmp = new int[a.length];\n" + " int rightIndex = mid + 1;\n" + " int tmpIndex = left;\n" + " int cIndex=left;\n" + " /**\n" + " * 左右逐个比较大小\n" + " */\n" + " while(left <=mid && rightIndex <= right) {\n" + " if (a[left] <= a[rightIndex])\n" + " tmp[tmpIndex++] = a[left++];\n" + " else\n" + " tmp[tmpIndex++] = a[rightIndex++];\n" + " }\n" + " /** 比较过程有可能左边剩余,将左边剩余的归并 */\n" + " while (left <=mid) {\n" + " tmp[tmpIndex++] = a[left++];\n" + " }\n" + " /** 比较过程有可能右边剩余,将右边剩余的归并 */\n" + " while ( rightIndex <= right ) {\n" + " tmp[tmpIndex++] = a[rightIndex++];\n" + " }\n" + "// System.out.println(\"第\"+(++number)+\"趟排序:\\t\");\n" + " /** 从临时数组拷贝到原数组 **/\n" + " while(cIndex<=right){\n" + " a[cIndex]=tmp[cIndex];\n" + " //输出中间归并排序结果\n" + " System.out.print(a[cIndex]+\"\\t\");\n" + " cIndex++;\n" + " }\n" + " System.out.println();\n" + " }"; }
package com.wangpos.datastructure.sort; 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; /** * Created by qiyue on 2017/11/23. */ public class ShellSortActivity extends BaseActivity { int array[] = {82 ,31 ,29 ,71, 72, 42, 64, 5,110}; @Override protected void initData() { setTitleText("希尔排序"); addItem(new CodeBean("希尔排序",shellSortCode)); } @Override protected String getTextData() { return Arrays.toString(array); } @Override protected int getImageData() { return 0; } @Override protected String getResultData() { shellsort(array); return Arrays.toString(array); } @Override protected String getTimeData() { return "O(n^2)"; } @Override protected String getSpaceTimeData() { return null; } @Override protected String getWendingXingData() { return "不稳定"; } @Override protected String getSummaryData() { return "希尔排序就是对直接插入排序的一个优化。好比有这么一种状况:对一个无序数组进行从小到大的排序,可是数组的最后一个位置的数是最小的,咱们要把它挪到第一个位置,其余位置的都要日后移动,要是这个数组很是大,那么直接插入排序的开销就很是大。\n" + "\n" + " 如今有一个array,希尔排序就是设定一个增量incrementNum(0<incrementNum<array.length)。\n" + "\n" + " 先从array[0]开始,以incrementNum为增量的进行直接插入排序,直到数组末尾,而后从array[1]开始重复:以incrementNum为增量的进行直接插入排序; 而后从array[1]开始重复......一直到array[n]。\n" + "\n" + " 而后取一个小于上一步增量的新的增量(好比设置为incrementNum/2),对前一个步骤的结果array进行遍历,直接插入排序....\n" + "\n" + " 再取小于上一步增量的新的增量,重复进行:遍历,直接插入排序\n" + "\n" + " 直到新的增量小于1以后再退出循环\n" + "\n" + " 步骤1:好比如今有数组{82 ,31 ,29 ,71, 72, 42, 64, 5,110} 第一次取增量设置为array.length/2 = 4 先从82开始以4为增量遍历直到末尾,获得(82,42) 排序获得{42 ,31 ,29 ,71, 72, 82, 64, 5,110}。 而后从第二个数31开始重复上一个步骤,获得(31,64) 排序获得{42 ,31 ,29 ,71, 72, 82, 64, 5,110}....... 以4为增量的遍历完数组以后,获得的结果是{42 ,31,5,71,72,82,64,29,110}\n" + "\n" + " 而后从新区增量,这儿设定为incrementNum/2 = 2,对{42 ,31,5,71,72,82,64,29,110}重复步骤1。 完事以后,在取新的增量,重复步骤1。 直到取到的增量小于1,退出循环。"; } private static final String shellSortCode = "/**\n" + " *\n" + " * @param arrays 须要排序的序列\n" + " */\n" + " public static void shellsort(int[] arrays) {\n" + " if (arrays == null || arrays.length <= 1) {\n" + " return;\n" + " }\n" + " //增量\n" + " int incrementNum = arrays.length / 2;\n" + " while (incrementNum >= 1) {\n" + " for (int i = 0; i < arrays.length; i++) {\n" + " //进行插入排序\n" + " for (int j = i; j < arrays.length - incrementNum; j = j + incrementNum) {\n" + " if (arrays[j] > arrays[j + incrementNum]) {\n" + " int temple = arrays[j];\n" + " arrays[j] = arrays[j + incrementNum];\n" + " arrays[j + incrementNum] = temple;\n" + " }\n" + " }\n" + " }\n" + " //设置新的增量\n" + " incrementNum = incrementNum / 2;\n" + " }\n" + " }"; /** * 希尔排序 * * @param arrays 须要排序的序列 */ public static void shellsort(int[] arrays) { if (arrays == null || arrays.length <= 1) { return; } //增量 int incrementNum = arrays.length / 2; while (incrementNum >= 1) { for (int i = 0; i < arrays.length; i++) { //进行插入排序 for (int j = i; j < arrays.length - incrementNum; j = j + incrementNum) { if (arrays[j] > arrays[j + incrementNum]) { int temple = arrays[j]; arrays[j] = arrays[j + incrementNum]; arrays[j + incrementNum] = temple; } } } //设置新的增量 incrementNum = incrementNum / 2; } } }
回复关键字:android
一、回复 “10” 查看 最有价值的10个spring boot开源项目git
二、回复 “国旗” 获取国旗头像教程**程序员
三、回复 “Ubuntu” 获取**100 个最佳 Ubuntu 应用 和 linux神器github
四、回复 “idea” 获取**最新idea破解教程 和 装逼神奇面试
五、回复 “ssh” 获取史上最好的 ssh工具 支持mac算法
六、回复 “代金券” 免费获取腾讯云和阿里云代金券
推荐阅读:
Java 开发人员经常使用的服务配置(Nginx、Tomcat、JVM、Mysql、Redis)
面试官问我:一个 TCP 链接能够发多少个 HTTP 请求?我居然回答不上来..
个人官网
个人CSDN地址http://blog.csdn.net/chenjian...
个人简书地址http://www.jianshu.com/u/9b5d...
个人githubhttps://github.com/javanan
个人码云地址https://gitee.com/jamen/
阿里云优惠券与阿里云上云教程<http://aliyun.guan2ye.com/>
我的微信公众号: dou_zhe_wan
欢迎关注
免责声明:
1.本公众号所转载文章均来自公开网络。
2.若是出处标注有误或侵犯到原著做者权益,请联系删除。
3.转载本公众号中的文章请注明原文连接和做者,不然产生的任何版权纠纷均与本公众号无关。