整理知识笔记(5)--数据结构(存储结构、排序)

1:释放堆栈空间

Image[] img = new Image[10];

for(int i=0;i<img.length;i++){

img[i]=Image.createImage(“/res/”+i+”.png”);

}

img = null;

上述代码没有释放堆栈空间;

1:img = null;后没有原img数组中的那些图片的引用,那么无法引用这些内存中的对象,所以gc可以把他们列为可回收的对象,因此没有必要遍历数组逐个清除。

2:但是显式调用gc是没有必要的,清空引用时候必要的;  正确释放代码如下:

if( img != null){

   for(int i=0;i<img.length;i++){

扫描二维码关注公众号,回复: 2864130 查看本文章

if( img [i] !=null){

  img[i]=null;

}

}img=null;

}

 

2:四种存储结构:

1)随机存储,即可以随意直接存储任意一个元素,根据下标直接存储元素(如数组)。如内存,直接通过地址直接访问任意空间。

2)顺序存储,只能从前到后逐个访问,如链表,必须从表头开始,向后逐个搜索,即为顺序存储

3)索引存储,为某个关键字建立索引表,从表中得到地址,再直接访问。

4)散列存储,是建立散列表,他相当于一种索引。

 

3:有1千万条重复的短信,以文本形式保存,一行一条,可以重复。使用5分钟选出重复出现最多的前10条短信。

(如果使用数据库利用select语句绝对不能再5分钟内完成的;所以使用数据库不行)

方法1:用哈希表的方法。将1千万条短信分成若干组,进行边扫描边建立散列表的方法。第一次扫描,取首字母、尾字节、中间任意两字节作为hash code(此作为一个所以去插入table),插入到hash table中,并记录其地址、信息长度、重复次数。相同的hash code且信息长度相同的就疑似相同,相同记录只加1次进hash table,但是重复次数加1,。经过一次扫描后,已经记录各自的重复次数,进行第二次扫描,对hash table进行处理。这样可以在线性的情况下O(n)完成10条信息的查找。分组后每组的top10 必须保证各不相同,可以使用hash来保证,也可以直接按照hash值得大小来分类。

 

方法2:采用小到大的排序的办法。(因为一般情况下字数越少重复的概率就越高,所以应该从小到大开始);;即一开始从一个字的短信开始找比较重复次数最多的top10分别记录出现的次数;然后开始找两个字的短信以此类推;当寻找信息的长度较长时,除了hash算法外,可以选择抽取头中尾等位置进行判断(疑似相同),此方法只是为了加快速度,但是不保证正确的top10,因此需要做标记。即如果出现两个top10里都有信息A(即发现此次top10中有已经做过标记的信息,那么就需要对这些两个top10里的所有信息进行精确搜索;如果没有遇到这种情况就可以投机取巧)则对其对应的字数的所有短信进行精确搜索,找到真正的top10进行比较。

 

方法3:采用内存映射的方法。(因为1千万条短信不会超过1GB空间,使用内存映射文件比较合适,可以一次映射,如果有更大数据量,就使用分段映射),因为不需要频繁使用文件I/O频繁分配小内存,这将大大提高了数据的加载速度。其次,对每条信息的第i个字母按照ASCII码进行分组,也就是创建树,i是树的深度,也是短信的低i个字母。

该问题主要解决两方面的问题:一方面内容加载;二是短信内容的比较。采用文本内存映射技术可以解决内容加载的性能问题(不仅仅不需要调用I/O函数,而且也不需要每读出一条信息都要分配内存)。而使用树技术可以有效减少比较次数。

 

4:trie树,(字典树)

又称单词查找树,字典树,是一种树形结构,是一种哈希树的变种,一种用于快速检索的多叉树结构;应用于统计和排序大量的字符串,所以经常被搜索引擎系统用于文本词频统计。

优点:最大限度减少字符串的比较,查询效率比哈希表高;

Trie的核心思想的空间换时间,利益字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。

对于搜索引擎,一般会保留一个单词查找树的前N个字,对于每个用户,保持trie最近前N个字为该用户使用的结果。(树的前N个字与用户输入的前N个字匹配相同)

如果用户点击任何搜索结果,trie可以迅速并异步获取完整的部分、模糊查找,然后预取数据,在用一个web应用程序可以发送一个较小的一组结果的浏览器。

应用:每输入一个关键字,就会出现搜索建议(如“北京”,就会出现北京天气、北京交通等以北京为前缀的搜索词)

 

5:排序:

稳定的排序

时间复杂度

空间复杂度

冒泡排序

差、平均是O(n2);好是O(n)

1

插入排序

差、平均是O(n2);好是O(n)

1

二分插入排序

差、平均是O(n2);好是O(n)

1

归并排序

都是O(nlogn)

O(n)

二叉树排序

都是O(nlogn)

O(n)

 

不稳定的排序

时间复杂度

空间复杂度

选择排序

都是O(n2)

1

希尔排序

好O(n);平均O(n1.25);坏O(n2)

1

堆排序

都是O(nlogn)

1

快速排序

最坏是O(n2),平均、好都是O(nlogn)

O(logn)

 

按照平均时间将排序分为四类:

1)平方阶排序O(n2):称简单排序,如直接插入、选择排序、冒泡排序;

2)线性对数阶排序O(nlogn):如快速、堆、归并排序;

3)O(n1+m)阶排序:0<m<1,如希尔排序;

4)线性阶排序O(n):如桶、基数排序;

 

简单排序中直接插入最好;快速排序最快;当文件正序时,直接插入和冒泡排序最佳;

 

不同条件下,排序方法的选择:

1)若n较小,(n<=50);可以采用直接插入或选择排序; 当规模较小时,优先选择直接插入;

2)若文件有序,则插入排序、冒泡排序、随机的快速排序为宜;

3)若n较大时,最好选择O(nlogn)的:快速、堆、归并排序;

 

快速排序最好,时间平均最短

堆所需空间最少

归并排序在对于单个元素排序情况是不好的(即归并排序的大小为每组2个或3个等比较时);归并排序与插入排序可以结合一起使用,即利用插入排序先求得较长的有序子文件,使得每组元素个数达到一定的数量,在进行归并排序进行归并;

 

6:

冒泡排序

void Bubblesort(in[]  num){

Boolean flag;//作为是否有swap的标志,要是在第i趟排序时没有发生swap,那么就默认为已经排好序了,后面的就不需要执行了

for(int i=num.length-1;i>0;i++){//每次i循环后都可以得到一个第i大的并且固定放于第i个位置,继续排0 ~ i-1位置的元素

flag=false; //每趟对(0 ~ i)位置的元素进行排序前,flag设为false

for(int j=0;j<i;j++){//每次j循环都是相邻两个比较交换,大的放右边 

if(num[j]>num[j+1]){//下标为0,1  1,2  2,3  3,4....相互比较

swap(num,j,j+1);

flag=true;//发生了交换,就设为true

}

}

  if(!flag){ return ;}//说明没有发生swap,即已经排好序,就结束程序;

}}

1)最好的时间复杂度:O(n)

文件正序,一次扫描即可完成排序;所需的关键字比较次数C和记录移动次数M均最小;(C=n-1;M=0;)

2)最坏的时间复杂度:O(n2)

文件反序,需要n-1趟i排序;每次排序都需要进行n-i次关键字的比较( 1<=i<=n-1 );所需的关键字比较次数C和记录移动次数M均最大;C=n(n-1)/2=O(n2); M=3n(n-1)/2=O(n2)

3)平均的时间复杂度:O(n2)

移动次数多,所以时间性能要比插入排序差

 

选择排序

插入排序

void selectsort(int num){

for(int i=0;i<num.length;i++){//将i+1 ~ n位置(即未排序)的元素一次与i元素比较,每次循环将最小的放入固定i位置

for(int j=i+1;j<num.length;j++){

if(num[i]>num[j]){//依次选择最小的元素

swap(num,i,j);

}

} } }

void insertsort(int num){

for(int i=1;i<num.length;i++){///每次循环对0~i位置的元素进行排序;(前提是0~i-1已经排序),只需要将i元素插入合适的位置

for(int j=i;(j>0)&&(num[j]<num[j-1]);j--){

swap(num,j,j-1);

}

}

}

 

Shell排序

希尔排序是插入排序的一种,shell排序的中心思想是将数据进行分组,然后对每一组进行排序,对每一组数据都有序后,就可以对所有的分组利用插入排序进行最后一次排序,可以减少数据交换的次数;

void Shell(int []a){

int index=a.length-1;

Boolean flag;///数据是否改变

int dlen = index/2;///初始集合间隔长度

while(dlen!=0){

for(j=dlen;j<index;j++){///对各个集合进行处理(根据间隔来分组)

flag=false;

temp=a[j];

p=j-dlen;//计算处理的位置

while(temp<a[p]&&p>=0&&p<=index){

a[p+dlen] = a[p];//a[j]=a[p],a[j]存大值

p=p-dlen;//计算该组中下一个处理的位置

flag=true;//发生改变

 if(p<0||p>index){ break;}//该组的所有元素遍历结束

a[p+dlen]=temp;//与最后的数值进行交换

if(flag){//打印目前排序结果

System.out.print("排序中:");

for(k=0;k<index;k++){

System.out.printf("%3s ",a[k]);

}

System.out.println("");

}

}

dlen = dlen/2;//计算下一次分割长度

}

} }

 

shell排序算法:

void ShellPass(int []R, int d){///希尔排序中的一趟排序,d为增量

for(i=d+1;i<n;i++){///将R[d+1~n]分别插入到当前的有序区

if(R[i]<R[i-d]){///如果右边小于左边

R[0]=R[i];j=i-d;///R[0]只是暂存单元

do{///查找R[i]的插入位置

R[j+d]=R[j];///i=i-d,将大的放到右边

j=j-d;///继续比较改分组中前一个

}while(j>0&&R[0]<R[j]);

R[j+d]=R[0];///将R[i]插入到之前的位置

}

}

}

void shellsort(int []R){

int increment=n;///设置初值

do{

increment=increment/2+1;///求下一个增量

ShellPass(R,increment);///一次增量为increment的shell排序

}while(increment>1)

}

Shell排序时间性能优于直接插入排序:

1)当文件初始有序时,直接插入排序需要的比较和移动次数均较少;

2)当希尔排序开始时增量较大,分组多,每组的记录少,故各组内的直接插入较快,后来增量逐渐减少,而各组的记录数目逐渐增多,但文件也接近有序状态,所以新的排序也会越来越快,所以希尔排序效率上要搞很多。

·希尔排序是不稳定的,即上图中的两个49的顺序有发生变化(原始与结果)

 

二分排序

其实就是折半插入排序

当插入某一元素i时,前边i-1个元素已经排好序,将i使用二分法插入其中;由于折半查找使得比较次数减少,数量级为O(nlogn),但是元素移动的次数为O(n2),所以此时间复杂度仍为O(n2),此为稳定排序。

 

for(i=1;i<10;i++){

temp=a[i];

low=0; high=i-1;

while(low<=high){///折半寻找a[i]应该存放的位置

mid=(low+high)/2;

if(a[mid]>temp)

high=mid-1;

else

low=mid+1;

}

for(j=i-1;j<high;j++)

a[j+1]=a[j];///将元素全部后移动,腾出位置

a[high+1]=temp;

}

 

快速排序

void qsort(int s[],int low,int high){

if(low<high){

i=low; j=high;  x=s[i];

while(i<j){

while(i<j&&s[j]>x){  j--;  }//从右到左遍历,找到第一个小的

if(i<j){   s[i]=s[j];  i++;  }

while(i<j&&s[i]<x){  i++;  }//从左到右遍历,找到第一个大的

if(i<j){ s[j]=s[i];   j--;  }

}

s[i]=x;

qsort(s,low,i-1);

qsort(s,i+1,high);

}

}

 

qsort(s,0,s.length-1);

归并排序

void merge(int array[],int L1,int R1,int L1,int R2){

i=L1;j=L2

int k=0;int []temp=new int [R2-L1+1];

while(i<=R1&&j<=R2){

if(array[i]>array[j++])

temp[k++]=array[j++];

else

temp[k++]=array[i++];

}

while(i<=R1){ temp[k++]=array[i++]; }

while(j<=R2){ temp[k++]=array[j++]; }

k=L1;

for(int  ele:temp){ array[k++]=ele; }

}

void mergesort(int array[],int L,int R){

if(L<R){

int mid=(L+R)/2;

mergesort(array,L,mid);

mergesort(array,mid+1,R);

merge(array,L,mid,mid+1,R);

}

}

mergesort(array,0,array.length-1);

 

 

 

 

快速排序采用一种分治的策略;

是一种不稳定的排序方法,平均复杂的为O(nlogn/log2)即O(nlogn),在最差情况下为O(n2)

归并排序都是O(nlogn)

 

 

 

猜你喜欢

转载自blog.csdn.net/qq_39667655/article/details/81810807