java gsp_GSP序列模式分析算法
介绍
GSP算法是序列模式挖掘算法的一种,他是一种类Apriori的一种,整个过程与Apriori算法比较类似,不过在细节上会略有不同,在下面的描述中,将会有所描述。GSP在原有的频繁模式定义的概念下,增加了3个的概念。
1、加入时间约束min_gap,max_gap,要求原来的连续变为只要满足在规定的min_gap到max_gap之间即可。
2、加入time_windows_size,只要在windows_size内的item,都可以被认为是同一ItemSet。
3、加入分类标准。
以上3点新的中的第一条特征将会在后面的算法中着重展现。
算法原理
1、根据所输入的序列,找出所有的单项集,即1频繁模式,这里会经过最小支持度阈值的判断。
2、根据1频繁模式进行连接运算,产生2频繁模式,这里会有进行最小阈值的判断。
3、根据2频繁模式连接产生3频繁模式,会经过最小支持度判断和剪枝操作,剪枝操作的原理在于判断他的所有子集是否也全是频繁模式。
4、3频繁模式不断的挖掘知道不能够产生出候选集为止。
连接操作的原理
2个序列,全部变为item列表的形式,如果a序列去掉第1个元素后,b序列去掉最后1个序列,2个序列的item完全一致,则代表可以连接,由b的最后一个元素加入到a中,至于是以独立项集的身份加入还是加入到a中最后1个项集中取决于b中的最后一个元素所属项集是否为单项项集。
时间约束计算
这个是用在支持度计数使用的,GSP算法的支持度计算不是那么简单,比如序列判断<2, <3, 4>>是否在序列, 2>,这就不能仅仅判断序列中是否只包含2,<3, 4>就行了,还要满足时间间隔约束,这就要把2,和<3,4>的所有出现时间都找出来,然后再里面找出一条满足时间约束的路径就算包含。时间的定义是从左往右起1.2,3...继续,以1个项集为单位,所有2的时间有2个分别为t=2和t=4,然后同理,因为<3,4>在序列中只有1次,所以时间为t=3,所以问题就变为了下面一个数组的问题
2 4
3
从时间数组的上往下,通过对多个时间的组合,找出1条满足时间约束的方案,这里的方案只有2-3,4-3,然后判断时间间隔,如果存在这样的方式,则代表此序列支持所给定序列,支持度值加1,这个算法在程序的实现中是比较复杂的。
算法的代码实现
测试数据输入(格式:事务ID item数 item1 item2.....):
1 2 1 5
1 1 2
1 1 3
1 1 4
2 1 1
2 1 3
2 1 4
2 2 3 5
3 1 1
3 1 2
3 1 3
3 1 4
3 1 5
4 1 1
4 1 3
4 1 5
5 1 4
5 1 5最后组成的序列为:
<1 3 4 (3,5)>
<1 2 3 4 5>
<1 3 5>
<4 5>
也就是说同一序列都是同事务的。下面是关键的类
Sequence.java:
package DataMining_GSP;
import java.util.ArrayList;
/**
* 序列,每个序列内部包含多组ItemSet项集
*
* @author lyq
*
*/
public class Sequence implements Comparable, Cloneable {
// 序列所属事务ID
private int trsanctionID;
// 项集列表
private ArrayList itemSetList;
public Sequence(int trsanctionID) {
this.trsanctionID = trsanctionID;
this.itemSetList = new ArrayList<>();
}
public Sequence() {
this.itemSetList = new ArrayList<>();
}
public int getTrsanctionID() {
return trsanctionID;
}
public void setTrsanctionID(int trsanctionID) {
this.trsanctionID = trsanctionID;
}
public ArrayList getItemSetList() {
return itemSetList;
}
public void setItemSetList(ArrayList itemSetList) {
this.itemSetList = itemSetList;
}
/**
* 取出序列中第一个项集的第一个元素
*
* @return
*/
public Integer getFirstItemSetNum() {
return this.getItemSetList().get(0).getItems().get(0);
}
/**
* 获取序列中最后一个项集
*
* @return
*/
public ItemSet getLastItemSet() {
return getItemSetList().get(getItemSetList().size() - 1);
}
/**
* 获取序列中最后一个项集的最后一个一个元素
*
* @return
*/
public Integer getLastItemSetNum() {
ItemSet lastItemSet = getItemSetList().get(getItemSetList().size() - 1);
int lastItemNum = lastItemSet.getItems().get(
lastItemSet.getItems().size() - 1);
return lastItemNum;
}
/**
* 判断序列中最后一个项集是否为单一的值
*
* @return
*/
public boolean isLastItemSetSingleNum() {
ItemSet lastItemSet = getItemSetList().get(getItemSetList().size() - 1);
int size = lastItemSet.getItems().size();
return size == 1 ? true : false;
}
@Override
public int compareTo(Sequence o) {
// TODO Auto-generated method stub
return this.getFirstItemSetNum().compareTo(o.getFirstItemSetNum());
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
/**
* 拷贝一份一模一样的序列
*/
public Sequence copySeqence(){
Sequence copySeq = new Sequence();
for(ItemSet itemSet: this.itemSetList){
copySeq.getItemSetList().add(new ItemSet(itemSet.copyItems()));
}
return copySeq;
}
/**
* 比较2个序列是否相等,需要判断内部的每个项集是否完全一致
*
* @param seq
* 比较的序列对象
* @return
*/
public boolean compareIsSame(Sequence seq) {
boolean result = true;
ArrayList itemSetList2 = seq.getItemSetList();
ItemSet tempItemSet1;
ItemSet tempItemSet2;
if (itemSetList2.size() != this.itemSetList.size()) {
return false;
}
for (int i = 0; i < itemSetList2.size(); i++) {
tempItemSet1 = this.itemSetList.get(i);
tempItemSet2 = itemSetList2.get(i);
if (!tempItemSet1.compareIsSame(tempItemSet2)) {
// 只要不相等,直接退出函数
result = false;
break;
}
}
return result;
}
/**
* 生成此序列的所有子序列
*
* @return
*/
public ArrayList createChildSeqs() {
ArrayList childSeqs = new ArrayList<>();
ArrayList tempItems;
Sequence tempSeq = null;
ItemSet tempItemSet;
for (int i = 0; i < this.itemSetList.size(); i++) {
tempItemSet = itemSetList.get(i);
if (tempItemSet.getItems().size() == 1) {
tempSeq = this.copySeqence();
// 如果只有项集中只有1个元素,则直接移除
tempSeq.itemSetList.remove(i);
childSeqs.add(tempSeq);
} else {
tempItems = tempItemSet.getItems();
for (int j = 0; j < tempItems.size(); j++) {
tempSeq = this.copySeqence();
// 在拷贝的序列中移除一个数字
tempSeq.getItemSetList().get(i).getItems().remove(j);
childSeqs.add(tempSeq);
}
}
}
return childSeqs;
}
}ItemSet.java:
package DataMining_GSP;
import java.util.ArrayList;
/**
* 序列中的子项集
*
* @author lyq
*
*/
public class ItemSet {
/**
* 项集中保存的是数字项数组
*/
private ArrayList items;
public ItemSet(String[] itemStr) {
items = new ArrayList<>();
for (String s : itemStr) {
items.add(Integer.parseInt(s));
}
}
public ItemSet(int[] itemNum) {
items = new ArrayList<>();
for (int num : itemNum) {
items.add(num);
}
}
public ItemSet(ArrayList itemNum) {
this.items = itemNum;
}
public ArrayList getItems() {
return items;
}
public void setItems(ArrayList items) {
this.items = items;
}
/**
* 判断2个项集是否相等
*
* @param itemSet
* 比较对象
* @return
*/
public boolean compareIsSame(ItemSet itemSet) {
boolean result = true;
if (this.items.size() != itemSet.items.size()) {
return false;
}
for (int i = 0; i < itemSet.items.size(); i++) {
if (this.items.get(i) != itemSet.items.get(i)) {
// 只要有值不相等,直接算作不相等
result = false;
break;
}
}
return result;
}
/**
* 拷贝项集中同样的数据一份
*
* @return
*/
public ArrayList copyItems() {
ArrayList copyItems = new ArrayList<>();
for (int num : this.items) {
copyItems.add(num);
}
return copyItems;
}
}GSPTool.java(算法工具类):
package DataMining_GSP;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* GSP序列模式分析算法
*
* @author lyq
*
*/
public class GSPTool {
// 测试数据文件地址
private String filePath;
// 最小支持度阈值
private int minSupportCount;
// 时间最小间隔
private int min_gap;
// 时间最大间隔
private int max_gap;
// 原始数据序列
private ArrayList totalSequences;
// GSP算法中产生的所有的频繁项集序列
private ArrayList totalFrequencySeqs;
// 序列项数字对时间的映射图容器
private ArrayList>> itemNum2Time;
public GSPTool(String filePath, int minSupportCount, int min_gap,
int max_gap) {
this.filePath = filePath;
this.minSupportCount = minSupportCount;
this.min_gap = min_gap;
this.max_gap = max_gap;
totalFrequencySeqs = new ArrayList<>();
readDataFile();
}
/**
* 从文件中读取数据
*/
private void readDataFile() {
File file = new File(filePath);
ArrayList dataArray = new ArrayList();
try {
BufferedReader in = new BufferedReader(new FileReader(file));
String str;
String[] tempArray;
while ((str = in.readLine()) != null) {
tempArray = str.split(" ");
dataArray.add(tempArray);
}
in.close();
} catch (IOException e) {
e.getStackTrace();
}
HashMap mapSeq = new HashMap<>();
Sequence seq;
ItemSet itemSet;
int tID;
String[] itemStr;
for (String[] str : dataArray) {
tID = Integer.parseInt(str[0]);
itemStr = new String[Integer.parseInt(str[1])];
System.arraycopy(str, 2, itemStr, 0, itemStr.length);
itemSet = new ItemSet(itemStr);
if (mapSeq.containsKey(tID)) {
seq = mapSeq.get(tID);
} else {
seq = new Sequence(tID);
}
seq.getItemSetList().add(itemSet);
mapSeq.put(tID, seq);
}
// 将序列图加入到序列List中
totalSequences = new ArrayList<>();
for (Map.Entry entry : mapSeq.entrySet()) {
totalSequences.add((Sequence) entry.getValue());
}
}
/**
* 生成1频繁项集
*
* @return
*/
private ArrayList generateOneFrequencyItem() {
int count = 0;
int currentTransanctionID = 0;
Sequence tempSeq;
ItemSet tempItemSet;
HashMap itemNumMap = new HashMap<>();
ArrayList seqList = new ArrayList<>();
for (Sequence seq : totalSequences) {
for (ItemSet itemSet : seq.getItemSetList()) {
for (int num : itemSet.getItems()) {
// 如果没有此种类型项,则进行添加操作
if (!itemNumMap.containsKey(num)) {
itemNumMap.put(num, 1);
}
}
}
}
boolean isContain = false;
int number = 0;
for (Map.Entry entry : itemNumMap.entrySet()) {
count = 0;
number = (int) entry.getKey();
for (Sequence seq : totalSequences) {
isContain = false;
for (ItemSet itemSet : seq.getItemSetList()) {
for (int num : itemSet.getItems()) {
if (num == number) {
isContain = true;
break;
}
}
if(isContain){
break;
}
}
if(isContain){
count++;
}
}
itemNumMap.put(number, count);
}
for (Map.Entry entry : itemNumMap.entrySet()) {
count = (int) entry.getValue();
if (count >= minSupportCount) {
tempSeq = new Sequence();
tempItemSet = new ItemSet(new int[] { (int) entry.getKey() });
tempSeq.getItemSetList().add(tempItemSet);
seqList.add(tempSeq);
}
}
// 将序列升序排列
Collections.sort(seqList);
// 将频繁1项集加入总频繁项集列表中
totalFrequencySeqs.addAll(seqList);
return seqList;
}
/**
* 通过1频繁项集连接产生2频繁项集
*
* @param oneSeq
* 1频繁项集序列
* @return
*/
private ArrayList generateTwoFrequencyItem(
ArrayList oneSeq) {
Sequence tempSeq;
ArrayList resultSeq = new ArrayList<>();
ItemSet tempItemSet;
int num1;
int num2;
// 假如将,2个1频繁项集做连接组合,可以分为
// 注意此时的每个序列中包含2个独立项集
for (int i = 0; i < oneSeq.size(); i++) {
num1 = oneSeq.get(i).getFirstItemSetNum();
for (int j = 0; j < oneSeq.size(); j++) {
num2 = oneSeq.get(j).getFirstItemSetNum();
tempSeq = new Sequence();
tempItemSet = new ItemSet(new int[] { num1 });
tempSeq.getItemSetList().add(tempItemSet);
tempItemSet = new ItemSet(new int[] { num2 });
tempSeq.getItemSetList().add(tempItemSet);
if (countSupport(tempSeq) >= minSupportCount) {
resultSeq.add(tempSeq);
}
}
}
// 上面连接还有1种情况是每个序列中只包含有一个项集的情况,此时a,b的划分则是
for (int i = 0; i < oneSeq.size(); i++) {
num1 = oneSeq.get(i).getFirstItemSetNum();
for (int j = i; j < oneSeq.size(); j++) {
num2 = oneSeq.get(j).getFirstItemSetNum();
tempSeq = new Sequence();
tempItemSet = new ItemSet(new int[] { num1, num2 });
tempSeq.getItemSetList().add(tempItemSet);
if (countSupport(tempSeq) >= minSupportCount) {
resultSeq.add(tempSeq);
}
}
}
// 同样将2频繁项集加入到总频繁项集中
totalFrequencySeqs.addAll(resultSeq);
return resultSeq;
}
/**
* 根据上次的频繁集连接产生新的侯选集
*
* @param seqList
* 上次产生的候选集
* @return
*/
private ArrayList generateCandidateItem(
ArrayList seqList) {
Sequence tempSeq;
ArrayList tempNumArray;
ArrayList resultSeq = new ArrayList<>();
// 序列数字项列表
ArrayList> seqNums = new ArrayList<>();
for (int i = 0; i < seqList.size(); i++) {
tempNumArray = new ArrayList<>();
tempSeq = seqList.get(i);
for (ItemSet itemSet : tempSeq.getItemSetList()) {
tempNumArray.addAll(itemSet.copyItems());
}
seqNums.add(tempNumArray);
}
ArrayList array1;
ArrayList array2;
// 序列i,j的拷贝
Sequence seqi = null;
Sequence seqj = null;
// 判断是否能够连接,默认能连接
boolean canConnect = true;
// 进行连接运算,包括自己与自己连接
for (int i = 0; i < seqNums.size(); i++) {
for (int j = 0; j < seqNums.size(); j++) {
array1 = (ArrayList) seqNums.get(i).clone();
array2 = (ArrayList) seqNums.get(j).clone();
// 将第一个数字组去掉第一个,第二个数字组去掉最后一个,如果剩下的部分相等,则可以连接
array1.remove(0);
array2.remove(array2.size() - 1);
canConnect = true;
for (int k = 0; k < array1.size(); k++) {
if (array1.get(k) != array2.get(k)) {
canConnect = false;
break;
}
}
if (canConnect) {
seqi = seqList.get(i).copySeqence();
seqj = seqList.get(j).copySeqence();
int lastItemNum = seqj.getLastItemSetNum();
if (seqj.isLastItemSetSingleNum()) {
// 如果j序列的最后项集为单一值,则最后一个数字以独立项集加入i序列
ItemSet itemSet = new ItemSet(new int[] { lastItemNum });
seqi.getItemSetList().add(itemSet);
} else {
// 如果j序列的最后项集为非单一值,则最后一个数字加入i序列最后一个项集中
ItemSet itemSet = seqi.getLastItemSet();
itemSet.getItems().add(lastItemNum);
}
// 判断是否超过最小支持度阈值
if (isChildSeqContained(seqi)
&& countSupport(seqi) >= minSupportCount) {
resultSeq.add(seqi);
}
}
}
}
totalFrequencySeqs.addAll(resultSeq);
return resultSeq;
}
/**
* 判断此序列的所有子序列是否也是频繁序列
*
* @param seq
* 待比较序列
* @return
*/
private boolean isChildSeqContained(Sequence seq) {
boolean isContained = false;
ArrayList childSeqs;
childSeqs = seq.createChildSeqs();
for (Sequence tempSeq : childSeqs) {
isContained = false;
for (Sequence frequencySeq : totalFrequencySeqs) {
if (tempSeq.compareIsSame(frequencySeq)) {
isContained = true;
break;
}
}
if (!isContained) {
break;
}
}
return isContained;
}
/**
* 候选集判断支持度的值
*
* @param seq
* 待判断序列
* @return
*/
private int countSupport(Sequence seq) {
int count = 0;
int matchNum = 0;
Sequence tempSeq;
ItemSet tempItemSet;
HashMap timeMap;
ArrayList itemSetList;
ArrayList> numArray = new ArrayList<>();
// 每项集对应的时间链表
ArrayList> timeArray = new ArrayList<>();
for (ItemSet itemSet : seq.getItemSetList()) {
numArray.add(itemSet.getItems());
}
for (int i = 0; i < totalSequences.size(); i++) {
timeArray = new ArrayList<>();
for (int s = 0; s < numArray.size(); s++) {
ArrayList childNum = numArray.get(s);
ArrayList localTime = new ArrayList<>();
tempSeq = totalSequences.get(i);
itemSetList = tempSeq.getItemSetList();
for (int j = 0; j < itemSetList.size(); j++) {
tempItemSet = itemSetList.get(j);
matchNum = 0;
int t = 0;
if (tempItemSet.getItems().size() == childNum.size()) {
timeMap = itemNum2Time.get(i).get(j);
// 只有当项集长度匹配时才匹配
for (int k = 0; k < childNum.size(); k++) {
if (timeMap.containsKey(childNum.get(k))) {
matchNum++;
t = timeMap.get(childNum.get(k));
}
}
// 如果完全匹配,则记录时间
if (matchNum == childNum.size()) {
localTime.add(t);
}
}
}
if (localTime.size() > 0) {
timeArray.add(localTime);
}
}
// 判断时间是否满足时间最大最小约束,如果满足,则此条事务包含候选事务
if (timeArray.size() == numArray.size()
&& judgeTimeInGap(timeArray)) {
count++;
}
}
return count;
}
/**
* 判断事务是否满足时间约束
*
* @param timeArray
* 时间数组,每行代表各项集的在事务中的发生时间链表
* @return
*/
private boolean judgeTimeInGap(ArrayList> timeArray) {
boolean result = false;
int preTime = 0;
ArrayList firstTimes = timeArray.get(0);
timeArray.remove(0);
if (timeArray.size() == 0) {
return false;
}
for (int i = 0; i < firstTimes.size(); i++) {
preTime = firstTimes.get(i);
if (dfsJudgeTime(preTime, timeArray)) {
result = true;
break;
}
}
return result;
}
/**
* 深度优先遍历时间,判断是否有符合条件的时间间隔
*
* @param preTime
* @param timeArray
* @return
*/
private boolean dfsJudgeTime(int preTime,
ArrayList> timeArray) {
boolean result = false;
ArrayList> timeArrayClone = (ArrayList>) timeArray
.clone();
ArrayList firstItemItem = timeArrayClone.get(0);
for (int i = 0; i < firstItemItem.size(); i++) {
if (firstItemItem.get(i) - preTime >= min_gap
&& firstItemItem.get(i) - preTime <= max_gap) {
// 如果此2项间隔时间满足时间约束,则继续往下递归
preTime = firstItemItem.get(i);
timeArrayClone.remove(0);
if (timeArrayClone.size() == 0) {
return true;
} else {
result = dfsJudgeTime(preTime, timeArrayClone);
if (result) {
return true;
}
}
}
}
return result;
}
/**
* 初始化序列项到时间的序列图,为了后面的时间约束计算
*/
private void initItemNumToTimeMap() {
Sequence seq;
itemNum2Time = new ArrayList<>();
HashMap tempMap;
ArrayList> tempMapList;
for (int i = 0; i < totalSequences.size(); i++) {
seq = totalSequences.get(i);
tempMapList = new ArrayList<>();
for (int j = 0; j < seq.getItemSetList().size(); j++) {
ItemSet itemSet = seq.getItemSetList().get(j);
tempMap = new HashMap<>();
for (int itemNum : itemSet.getItems()) {
tempMap.put(itemNum, j + 1);
}
tempMapList.add(tempMap);
}
itemNum2Time.add(tempMapList);
}
}
/**
* 进行GSP算法计算
*/
public void gspCalculate() {
ArrayList oneSeq;
ArrayList twoSeq;
ArrayList candidateSeq;
initItemNumToTimeMap();
oneSeq = generateOneFrequencyItem();
twoSeq = generateTwoFrequencyItem(oneSeq);
candidateSeq = twoSeq;
// 不断连接生产候选集,直到没有产生出侯选集
for (;;) {
candidateSeq = generateCandidateItem(candidateSeq);
if (candidateSeq.size() == 0) {
break;
}
}
outputSeqence(totalFrequencySeqs);
}
/**
* 输出序列列表信息
*
* @param outputSeqList
* 待输出序列列表
*/
private void outputSeqence(ArrayList outputSeqList) {
for (Sequence seq : outputSeqList) {
System.out.print("
for (ItemSet itemSet : seq.getItemSetList()) {
System.out.print("(");
for (int num : itemSet.getItems()) {
System.out.print(num + ",");
}
System.out.print("), ");
}
System.out.println(">");
}
}
}调用类Client.java:
package DataMining_GSP;
/**
* GSP序列模式分析算法
* @author lyq
*
*/
public class Client {
public static void main(String[] args){
String filePath = "C:\\Users\\lyq\\Desktop\\icon\\testInput.txt";
//最小支持度阈值
int minSupportCount = 2;
//时间最小间隔
int min_gap = 1;
//施加最大间隔
int max_gap = 5;
GSPTool tool = new GSPTool(filePath, minSupportCount, min_gap, max_gap);
tool.gspCalculate();
}
}算法的输出(挖掘出的所有频繁模式):
算法实现的难点
1、算法花费了几天的时间,难点首先在于对算法原理本身的理解,网上对于此算法的资料特别少,而且不同的人所表达的意思 都有少许的不同,讲的也不是很详细,于是就通过阅读别人的代码理解GSP算法的原理,我的代码实现也是参考了参考资料的C语言的实现。
2、在实现时间约束的支持度计数统计的时候,调试了一段时间,做时间统计容易出错,因为层级实在太多容易搞晕。
3、还有1个是Sequence和ItemSet的拷贝时的引用问题,在产生新的序列时一定要深拷贝1个否则导致同一引用会把原数据给改掉的。
GSP算法和Apriori算法的比较
我是都实现过了GSP算法和Apriori算法的,后者是被称为关联规则挖掘算法,偏向于挖掘关联规则的,2个算法在连接的操作上有不一样的地方,还有在数据的构成方式上,Apriori的数据会简单一点,都是单项单项构成的,而且在做支持度统计的时候只需判断存在与否即可。不需要考虑时间约束。Apriori算法给定K项集,连接到K-1项集算法就停止了,而GSP算法是直到不能够产生候选集为止。
java gsp_GSP序列模式分析算法相关推荐
- GSP序列模式分析算法
参考资料:http://blog.csdn.net/zone_programming/article/details/42032309 更多数据挖掘代码:https://github.com/liny ...
- java培训:Java的十大算法
想要学好java语言,就要打好基础,java要学习的东西有很多,今天小编就来和大家说下java的十大算法. 算法一:快速排序算法 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序 n 个 ...
- android studio插入数据表中没有_学Java能拿高薪吗 Java中常见排序算法有哪些
学Java能拿高薪吗?Java中常见排序算法有哪些?作为老牌编程语言,Java拥有广阔的市场占有率,几乎90%以上的大中型互联网应用系统在服务端开发都会首选Java.为了加入到Java这一高薪行业,很 ...
- java对象序列化为字符串
文章目录 1 场景 2 应用 2.1 过程描述 2.2 字节数组.字符串转换 2.2.1 hex转码 2.2.2 base64转码 2.3 对象序列化字节数组 2.3.1 使用commons-lang ...
- java 离散算法_用JAVA语言实现离散数学算法
用JAVA语言实现离散数学算法 用JAVA语言实现离散数学算法 * 显示离散数学算法的真值表 * 提供将一个中缀合适公式的真值表输出到某一PrintStream流中的功能 * 以单个大写字母表示变量( ...
- 数据结构与算法【Java】05---排序算法总结
前言 数据 data 结构(structure)是一门 研究组织数据方式的学科,有了编程语言也就有了数据结构.学好数据结构才可以编写出更加漂亮,更加有效率的代码. 要学习好数据结构就要多多考虑如何将生 ...
- Java回炉之排序算法
Java回炉之排序算法 Java回炉之排序算法 冒泡排序 插入排序 归并排序 快速排序 希尔排序 选择排序 堆排序 冒泡排序 一次次遍历数组,每次比较相邻两个. 两重循环,内层比较index和inde ...
- Java折半二叉树,成都汇智动力-Java实现常用排序算法
原标题:成都汇智动力-Java实现常用排序算法 排序算法介绍 1. 基本概念 稳定性: 待排序的数列中,若两个元素的值相等 R1 = R2 ,在排序结束之后,元素之间的相对位置没有发生变化,则称排序算 ...
- Java八大内部排序算法
排序算法的分类 时间复杂度以及空间复杂度 注:为了进行一定的比较,此处不按照上面的顺序. 直接插入排序 基本思想 在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使 ...
最新文章
- java 内存溢出 内存泄露_java 内存泄露、内存溢出、内存不足
- /dev/null 21 的作用
- uuid生成_php如何生成 uuid(总结)
- git编译安装与常见问题解决
- python读取配置文件 ConfigParser
- RSA大会:中国信息安全的“走出去”与“学回来”
- random number in groovy
- 验证码类库CaptchaMvc
- 美团一面:如何优化慢SQL?
- 一个屌丝程序猿的人生(三十九)
- doe五步法_DOE试验设计的五大步骤
- 梦想近在眼前却遥不可及
- 【腾讯TMQ】我们在外包资源池化管理走过的弯路
- 零基础入门CV - Task 03 字符识别模型.md
- 计算机硬盘空间不足怎么删,Win10硬盘空间不足?教你这样清理,瞬间多出10个G!...
- 版本管理工具-Git
- html初学者对相对地址,绝对地址的理解
- 线性异质双官能PEG试剂荧光素-聚乙二醇-巯基FITC-PEG-SH
- 第7周项目5 排队看病模拟
- From .1:从屏保到Win平台开发 - 一个可运行的C#屏保程序
热门文章
- 64位java_一文详解 Java 的八大基本类型!
- java null 转空_java 对象属性为 null 值转为 空串
- Service Mesh 为什么从“趋势”走向“无聊”?
- 云原生数据湖分析DLA 2020年年度总结
- JUST技术:提升基于GPS轨迹的路网推测精确度
- 上一局APP玩,你画我猜,作为灵魂画手从没服过谁
- mysql索引_效率测试(包含测试sql脚本300万条数据),可用作教学案例。
- preg_grep用法
- 创建自定义地理(坐标)变换
- 在linux学习中遇到的问题