概念:倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。带有倒排索引的文件我们称为倒排索引文件,简称倒排文件(inverted file)。
倒排文件: 倒排列表用来记录有哪些文档包含了某个单词。一般在文档集合里会有很多文档包含某个单词,每个文档会记录文档编号(DocID),单词在这个文档中出现的次数(TF)及单词在文档中哪些位置出现过等信息,这样与一个文档相关的信息被称做倒排索引项(Posting),包含这个单词的一系列倒排索引项形成了列表结构,这就是某个单词对应的倒排列表。右图是倒排列表的示意图,在文档集合中出现过的所有单词及其对应的倒排列表组成了倒排索引。
倒排索引=单词(关键字)+倒排文件(倒排项的集合)
/**
* 倒排项结构
*/
class InvertItem{
int docid;//项目编号
int freqs;//频率
List<Integer> list;//每个关键字在文件中出现的位置,这里用单词是文件中第几个单词
public InvertItem(int docid, int freqs, List<Integer> list) {
this.docid = docid;
this.freqs = freqs;
this.list = list;
}
}
/**
* 倒排列表结构
*/
class InvertList{
List<InvertItem> itemList;//倒排文件
public InvertList(){
itemList = new ArrayList<>();
}
}
/**
* 倒排索引的结构
*/
class InvertIndex {
Map<String, InvertList> invertIndexMap;
List<File> filesList;
String path;
public InvertIndex(){
invertIndexMap=new HashMap<String, InvertList>();
filesList=new ArrayList<File>();
}
//搜索所有的子文件
public void seacheFile(File file){
for (File file1 : file.listFiles()) {
if(file1.isDirectory()){
seacheFile(file1);
}
filesList.add(file1);
}
}
public void findpath(String path ){
System.out.println("开始搜索文件..............");
File file = new File(path);//打开路径
if (file.exists()) {//子文件
seacheFile(file);
}else{
System.out.print("路径错误");
}
}
// 接收用户输入的路径信息,为该路径下所有的文件建立倒排索引
public void createInvertINdex(String path) throws IOException {
String[] words = null;
BufferedReader reader=null;
try {
findpath(path);
int docid;//用file的在list中的下标作为倒排项的编号
for (docid = 0; docid < filesList.size(); docid++) {
reader = new BufferedReader(new FileReader(filesList.get(docid)));
String s = null;
Integer count=0;//标记
while ((s = reader.readLine()) != null) {
//获取单词
words = s.split(" ");
for (int j=0;j<words.length;j++) {
String string=words[j];
count++;
if (!invertIndexMap.containsKey(string)) {
// 如果map中没有包含单词,将单词和它对应的倒排文件添加到map
ArrayList list=new ArrayList<Integer>();
//创建list使存放单词在文件中出现的位置
list.add(count);
//创建单词存在文件的倒排项
InvertItem item = new InvertItem(docid,1,list);
//创建单词对应的倒排文件
InvertList invertList=new InvertList();
//将倒排项添加进去
invertList.itemList.add(item);
//将单词和倒排文件的映射关系添加进去
invertIndexMap.put(string, invertList);
} else {
InvertList invertList1 = invertIndexMap.get(string);
//如果已经存在,把倒排文件拿出来,判断该文件的倒排项是否已经存在
//这里本来直接用倒排项编号直接查询,但是会发生下标越界,
// 因为这里加入倒排项的时候是按顺序添加的,只需要判断倒排文件中最后一个倒排项的编号
//是否大于当前要判断是否存在的倒排项,如果大于则肯定存在,如果小于则不存在,需要创建倒排项
if(invertList1.itemList.size()-1>=docid){//如果已经存在
InvertItem item=invertList1.itemList.get(docid);
item.list.add(count);
}else{
ArrayList list=new ArrayList<Integer>();
//创建list使存放单词在文件中出现的位置
list.add(count);
//创建单词存在文件的倒排项
InvertItem item1 = new InvertItem(docid,1,list);
invertList1.itemList.add(item1);
}
}
}
}
}
}catch(Exception e){
e.printStackTrace();
}finally{
reader.close();
}
}
public void searchSentence(String str){
if(str==null){
System.out.print("the sentence is null,please input again");
}
String[] words=str.split(" ");//分割单词
List<InvertList> itemsLists=new ArrayList<InvertList>();//存放所有出现的倒排文件
for(int i=0;i<words.length;i++){
if(invertIndexMap.containsKey(words[i])){
itemsLists.add(invertIndexMap.get(words[i]));
}
}
//求倒排文件的交集
//思路:因为在倒排文件中排序的时候是根据文件编号有小到大进行排序的
//所以只需要将统计每个倒排文件中倒排项出现的次数是否和单词数量一致
int[] count=new int[filesList.size()];//存放所有出现的倒排项
List<InvertItem> desitem=new ArrayList<InvertItem>(filesList.size());
for(int j=0;j<itemsLists.size();j++){//遍历所有的倒排文件
InvertList list1=itemsLists.get(j);
for(int k=0;k<list1.itemList.size();k++){
InvertItem item=list1.itemList.get(k);
count[item.docid]++;
desitem.add(item.docid,item);
}
}
for(int i=0;i<count.length;i++){
if(words.length==count[i]){
System.out.print(str+" 出现在 :"+filesList.get(i));
System.out.print(" 文件编号为:"+i);
}
}
System.out.println();
}
}
// query,给用户提供查询搜索服务(关键词, 句子)
/*
1.哈希表
2.BST,AVL,RB,SkipList
3.字典树 remove操作,倒排索引(建立倒排索引,query)
动态规划
0-1背包
最长的公共子序列 lcs
最长的非降子序列 lis
最大子段和
图
广度优先搜索
深度优先搜索
最短路径
*/
/**
* 描述:倒排索引结构的测试
*
* @Author administrator(shilei)
* @Date 2018/7/21
*/
public class InvertIndexTest {
public static void main(String[] args) throws IOException {
String path="D:\\test";
InvertIndex invertIndex=new InvertIndex();
invertIndex.createInvertINdex(path);
invertIndex.searchSentence("I student");
/*Map map=invertIndex.invertIndexMap;
List list=invertIndex.filesList;
Iterator it=map.keySet().iterator();
while(it.hasNext()){
String word=(String)it.next();
System.out.println("单词:"+ word +"————————————————————————————————");
InvertList items=(InvertList) map.get(word);
for(int i=0;i<items.itemList.size();i++){
InvertItem item=items.itemList.get(i);
int docid=item.docid;
System.out.print(" 文件名:"+list.get(docid));
System.out.print(" 文件编号:"+item.docid);
for(int k=0;k<item.list.size();k++){
int g=k+1;
System.out.print(" 位置"+g+":"+item.list.get(k));
}
System.out.println();
}
}*/
}
}