SUNING

linux高性能服务器、模式语言识别

一个单链表,求倒数第N个元素,如何获得(保持一个距离头节点的变量);

,说一个大数组,先降序后升序,问怎么能最快找到中间的那个元素(二分)。 

求N个set的交集

如何遍历树来一层一层从左到右输出元素,当时想各种递归解法,都不对,后来说用队列存储每个遍历过的节点子节点,才知道这题可以用队列,晕倒。

实现一个算法从一个单链表中返回倒数第n个元素(keep it up)


我们维护两个指针, 它们之间的距离为n。然后,我将这两个指针同步地在这个单链表上移动,保持它们的距离 为n不变。那么,

当第二个指针指到空时,第一个指针即为所求。

[html]  view plain  copy
  1. #include <iostream>  
  2.   
  3. struct Node  
  4. {  
  5.     int   data;  
  6.     Node* next;  
  7. };  
  8.   
  9.   
  10. void initList(Node* vNode)  
  11. {  
  12.     for (int i=0; i < 20; ++i)  
  13.     {  
  14.         Node* TempNode = new Node;  
  15.         TempNode->data = i;  
  16.         TempNode->next = vNode->next;  
  17.         vNode->next    = TempNode;  
  18.     }  
  19. }  
  20.   
  21.   
  22.   
  23. Node* getNthBackWards(const Node* vNode, int vN)  
  24. {  
  25.     if (vN < 1 || vNode == NULL) return NULL;  
  26.     Node* p = vNode;  
  27.     Node* q = vNode;  
  28.   
  29.     while (vN>0)  
  30.     {  
  31.         ++q;  
  32.         if (q == NULL) return NULL;  
  33.         --vN;  
  34.     }  
  35.   
  36.     while (q != NULL)  
  37.     {  
  38.         ++q;  
  39.         ++p;  
  40.     }  
  41.   
  42.     return p;  
  43. }  
  44.   
  45. int main()  
  46. {  
  47.     Node* Root = new Node;  
  48.     Root->next = NULL;  
  49.   
  50.     initList(Root);  
  51.       
  52.     Node* Result = getNthBackWards(Root, 7);  
  53.     std::cout << Result->data << std::end;  
  54.   
  55.     return 0;  
  56. }  

#include<stdio.h>
#include<ctype.h>
int main()
{
int i,comp1(),comp2();
int num[10]={125,-26,53,12,-6,96,46,85,-45,785};
printf("the original arrat is :\n");
for(i=0;i<10;i++)
{
printf("%10d",num[i]);
}
qsort(num, 10, sizeof(int), comp1);
printf("\n,The accending sorted array is:\n");
for(i=0;i<10;i++)
{
printf("%10d",num[i]);
}
qsort(num, 10, sizeof(int), comp2);
printf("\n The decending sorted arrat is:\n");
for(i=0;i<10;i++)
{
printf("%10d",num[i]);
}
return 0;
}
comp1(int *i, int *j)
{
return *i-*j;
}
comp2(int *i, int *j)
{
return *j-*i;

}

寻找数组中的第K大的元素,多种解法以及分析

遇到了一个很简单而有意思的问题,可以看出不同的算法策略对这个问题求解的优化过程。
问题:寻找数组中的第K大的元素。

最简单的想法是直接进行排序,算法复杂度是O(N*logN)。这么做很明显比较低效率,因为不要求别的信息只要计算出第K大的元素。当然,如果在某种情况下需要频繁访问第K大的元素就可以先进行一次排序在直接得出结果。

第一种方式是这样,用选择排序,冒泡法,或者交换排序这类的排序,对前K个元素进行排序。这三种算法也许不是最快的排序算法。但是都有个性质:计算出最大(小)的元素的算法复杂度是O(N)。这个过程不能中断,要计算第三大的元素必须建立在已经算出第二大的元素的基础上(因为每次都是计算当前数组最大)。所以它的算法复杂度是O(N*K);

第二种方法是用快速排序的思想。快速排序每次把一个元素交换到正确的位置,同时把左边的都方上大的,右边都放上小的。这个算法每一次选取一个枢纽元,排序之后,查看枢纽元的位置。如果它的位置大于K,就说明,要求出前面一个子序列的第K大的元素。反之,如果小于K,就说明要求出在后面一个序列的第K - 前一个序列的长度个元素。

如此,就把这个问题改变成了一个可以用快排思想解决的问题。对于快速排序,算法复杂度是O(N*logN)。而这个算法的算法复杂度是O(N)。为什么呢?

其实这个地方的算法复杂度分析很有意思。第一次交换,算法复杂度为O(N),接下来的过程和快速排序不同,快速排序是要继续处理两边的数据,再合并,合并操作的算法复杂度是O(1),于是总的算法复杂度是O(N*logN)(可以这么理解,每次交换用了N,一共logN次)。但是这里在确定枢纽元的相对位置(在K的左边或者右边)之后不用再对剩下的一半进行处理。也就是说第二次插入的算法复杂度不再是O(N)而是O(N/2),这不还是一样吗?其实不一样,因为接下来的过程是1+1/2+1/4+........ < 2,换句话说就是一共是O(2N)的算法复杂度也就是O(N)的算法复杂度。

这个算法目前我在数据结构和算法书上和剑指Offer上都看到过。算是一种很经典很经典的算法。原因是因为他通过努力把算法复杂度在每次递归中下降一些,最终让整个算法的复杂度下降极多,算是一种十分聪明的做法。

第三种方法很是简单,但是使用它需要某个条件,也就是输入数组的取值范围很小,最好的情况是能形成完全分布,也就是1000大小的数组里面的数字是从1到1000这样子。首先,生成一个能够完全装下原数组的数组,这个地方的装下是指数组大小等于原数组最大元素(也许还有优化,但这么描述简单一点),比如原数组是[1,2,3,4,5],我要生成的数组大小是5,如果原数组是[5,3,6,10],我要生成的数组大小是10。接下来遍历原数组,把每一个元素放到第二个数组对应的下标处,5就放在下标为5的地方(实际过程中要减1,因为是数组从0开始)。放的过程中增加元素值用来统计这个元素出现的次数。这一过程算法复杂度是O(N)。接下来,再遍历生成的数组,找出第K大的元素。
这个过程的算法复杂度是多少呢?其实这个和原数组很有关系,原数组越离散也就越糟糕。比如原数组是[1,1000],这样就十分糟糕。第二部的算法复杂度是O(M),M是前数组的最大值。总的算法复杂度O(N)+O(M);

由此可见第三种方法在这个问题的处理非常不好。虽然第三种方法限制颇多(浮点型和负数还有对原数组大小的要求),但是第三种方法的实质是一种散列。就是把原来的映射关系变成了一种反映射。也就是说如果形成了数据与地址的直接映射。但是这种映射的问题也体现的很明显,它这么做也只能算是捡了个漏子,如果输入数组稍微一边,还是一样要用hash算法计算其hash值。再把hash值映射到地址上。

第四种方法是用二叉堆来做。对大小为N的数组构建二叉堆的算法复杂度是O(N)。然后每次下滤的算法复杂度是O(logN),一共下滤K次,算法复杂度是O(N+K*logN)。

这种做法比较适合用来处理输入数组极大的情况,原因是如果输入数组大到不能放入内存,那么构建二叉堆(优先队列)的时候就可以只构造一个K个元素的优先队列。如果下一个元素比这个最小堆的堆顶还小就直接pass。第二个原因是算法二在对付一个极大的输入队列的时候算法复杂度的一个常数会很大。

leetcode的题4:给定两个有序数组,求两个数组的中间值。

我的思路是,既然是有序数组,就可以按照归并排序法的思路,按照最后的归并过程。

建立一个新的数组,并对两个数组及归并数组目前的位置分别编号i,j,k。

将两个数组中较小的值推入i的位置,然后将被提取数据的数组及归并数组索引+1。

这个过程中需要考虑的问题是如果其中一个数组已经全部提取完成应如何处置。

以下是我的代码:

复制代码
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        size_t n = nums1.size(),m=nums2.size();
        vector<int> combine;
        int i=0,j=0,k=0;
        for(;i<m+n;++i){
            if(j<n&&k<m)
                combine.push_back(nums1[j]<nums2[k]?nums1[j++]:nums2[k++]);
            else
                combine.push_back(j==n?nums2[k++]:nums1[j++]);
        }
        if((m+n)%2==1)
            return combine[(m+n-1)/2];
        else
            return (combine[(m+n-1)/2]+combine[(m+n-1)/2+1])/2.0;
    }
};

这里先讲一种类似于快速排序的方法。注意题目要求,不要求完全排序,只要求最快解决问题!这个题是我面试NI公司时,对方问我的。原话是从1亿个数据里,找出前一百个最大的。


首先看源码吧:

[cpp]  view plain  copy
  1. void findMaxN(int a[], int start, int end, int N)//从数组a里,找出前N个最大的。如果是a[100],则start = 0, end = 99.注意这个索 引问题  
  2. {  
  3.     int mid = (start + end)/2;  
  4.   
  5.    int i = start, j = end;  
  6.   
  7. while(i<j)  
  8.   
  9. {  
  10.     while(i<j && a[i]<=a[mid])  
  11.   
  12.      i++;  
  13.   
  14.   while(i<j && a[j]>=a[mid])  
  15.   
  16.    j--;  
  17.   
  18. swap(a[i], a[j]);  
  19.   
  20. }  
  21. /*注意这个while出来之后,i一定是等于j的,且从i 到 end是较大的那一端*/  
  22.   
  23. if(end-i+1 == N)  
  24.   
  25. return;  
  26.   
  27. if(end - i+1 > N)  
  28.   
  29. findMaxN(a, i, end, N);  
  30.   
  31. else  
  32.   
  33.   findMaxN(a, start, i, N - (end -i +1));  
  34.   
  35. }  


     再来详细说说思路,如果您看懂了快速排序对此一定不会陌生。首先拿a[mid] 做基准值,然后让i, j从两端开始遍历,如果索引小的那一端数据小于基准值a[mid], 就往前遍历,如果 左边的大于了a[mid], while循环会跳出,记住这时的a[i] 是大于a[mid],

       然后类似的思路,从j那一端遍历,当右边的数据a[j ] 小于基准值a[mid],则小while循环会跳出。然后会运行swap()这个函数,将两个值进行交换 。 这样最外面的while循环出来之后,i一定是等于j的,注意这里i 和j不一定等于当前域中的mid。而且从i到end都是较大的,然后看看较大的那一端的数据有多少个,然后进行遍历。

      如果已经等于要找的N个,则跳出函数。如果大于N,则要从i到end为区间内接着找;如果小于N,比如说要找前50个最大的,结果end-i+1才等于20,也就是从i到end有20个较大的数,这就需要从 start(第一次时,可以认为是0)到i 区间内再找50-20 = 30个最大的。

    至于swap的函数,利用引用实现如下:

void swap(int &a, int &b)
{

   int temp = a;

   a = b;

   b= temp;

}


     最后说说,如果这个函数执行完毕了,我怎么访问找到的最大的N个数呢? 很简单,假设数组长度为n, 从a[n-1], a[n-2]。。。顺序取N个数,就是找到的最大的前N个数据了!这个算法的最大情况时间复杂度是o(n的平方),最好情况是o(n), 平坦下来也是o(n).

   等我空我介绍第二种思路,上面代码是即兴写的,源码我一会上传。

http://download.csdn.net/detail/yanzi1225627/4684046

================================================================================================================================================================================================================================================================================================================

今天工作中遇到一个问题:

要求对28G的数据排序,数据的格式如下:

id  time

要求按时间升序排序

已有的资源为64G内存,32核的服务器一台,需要在一个晚上(8小时)内跑出排序结果。

一个直观的解法就是把数据全部加载进内存,然而实际操作并不可行,才加载了1/10不到的数据就已经使用了接近40G的内存了。
另一个解决思路是手动进行map reduce,分成若干小模块,小模块排序之后再逐一合并。这个思路实现起来代价也很高,逐一合并无法利用32核GPU,速度估计不达标。充分利用CPU的话,调度策略编码量估计不小……

最后我们找到了一个超级巧妙的解决方案:

按照日期划分文件,每天的数据单独输出成一个文件,由于我们的数据只是1~8月份的数据,故只划分为240多个文件,
每个文件只有119M,然后利用多核特性对每个文件进行排序,排好序后按文件名收尾连接就好了。

桶排序思想,好巧妙!

================================================================================================================================================================================================================================================================================================================

#include <iostream>
 #include <string>
 #include <stdlib.h>
 #include <time.h>
 #include <stdio.h>
 #include <string.h>
 #include <io.h>
 using namespace std;
  
 const int memory_size = 2000000;//每次10万条数据
  
 int remove_files(char (*files)[30],int count)
 {
 for(int i=0;i<count;i++)
 {
 if (!strcmp(files[i],"data.txt\n"))
 continue;
 char del_cmd[50]={0};
 sprintf(del_cmd,"del %s",files[i]);
 system(del_cmd);
 }
 return 0;
 }
 //int listFodler(char* file,char (*files)[30])
 int listFodler(char (*files)[30])
 {
 //strcat(file,"\\data*.txt");
 char *file=".\\data*.txt";
 long handle;
 struct _finddata_t fileinfo;
 handle=_findfirst(file,&fileinfo);
 if(-1==handle)return -1;
 int i=0;
 sprintf(files[i++],"%s\n",fileinfo.name);
 while(!_findnext(handle,&fileinfo))
 {
 if(strcmp(fileinfo.name,".")==0||strcmp(fileinfo.name,"..")==0||fileinfo.attrib==_A_SUBDIR)
 {
 continue;
 }
 sprintf(files[i++],"%s\n",fileinfo.name);
 }
 _findclose(handle);
 return i;
 }
  
 int read_data(FILE *fp, char **space)
 {
 int index = 0;
 char s_tmp[30];
 while (index < memory_size && fscanf(fp, "%s\n", s_tmp) != EOF)
 {
 space[index]=new char[sizeof(s_tmp)+1];
 strcpy(space[index],s_tmp);
 index++;
 }
 return index;
 }
  
 void write_data(FILE *fp, char **space, int num)
 {
 int index = 0;
 while (index < num)
 {
 fprintf(fp, "%s\n", space[index]);
 index++;
 }
 }
  
 void check_fp(FILE *fp)
 {
 if (fp == NULL)
 {
 cout << "The file pointer is invalid!" << endl;
 exit(1);
 }
 }
  
 int compare(const void *first_num, const void *second_num)
 {
 return strcmp(*(char **)first_num,*(char **)second_num);
 }
  
 string new_file_name(int n)
 {
 char file_name[20];
 sprintf(file_name, "data%d.txt", n);
 return file_name;
 }
  
 int memory_sort(const char *filename)
 {
 FILE *fp_in_file = fopen(filename, "r");
 check_fp(fp_in_file);
 int counter = 0;
 while (true)
 {
 char *space[memory_size]={NULL};
 int num = read_data(fp_in_file, space);
 if (num == 0)
 break;
  
  
 qsort(space, num, sizeof(char *), compare);
  
 string file_name = new_file_name(++counter);
 FILE *fp_aux_file = fopen(file_name.c_str(), "w");
 check_fp(fp_aux_file);
  
 write_data(fp_aux_file, space, num);
 fclose(fp_aux_file);
  
 for(int i=0;i<num;i++)
 if (space[i] != NULL)
 {
 delete []space[i];
 space[i]=NULL;
 }
  
 }
 fclose(fp_in_file);
  
 return counter;
 }
  
 void merge_sort(int file_num,const char *filename)
 {
 if (file_num <= 0)
 return;
 FILE *fp_out_file = fopen(filename, "w");
 check_fp(fp_out_file);
  
 FILE **fp_array = new FILE *[file_num];
 int i;
 for (i = 0; i < file_num; i++)
 {
 string file_name = new_file_name(i + 1);
 fp_array[i] = fopen(file_name.c_str(), "r");
 check_fp(fp_array[i]);
 }
  
 char first_data[1000][30];
 bool *finish = new bool[file_num];
 memset(finish, false, sizeof(bool) * file_num);
  
 for (i = 0; i < file_num; i++)
 fscanf(fp_array[i], "%s\n", first_data[i]);
 while (true)
 {
 int index = 0;
 while (index < file_num && finish[index])
 index++;
  
 if (index >= file_num)
 break;
  
 char min_data[30];
 strcpy(min_data,first_data[index]);
 for (i = index + 1; i < file_num; i++)
 {
  
 int f=strcmp(min_data,first_data[i]);
 bool swap_f=false;
 if (f>0)
 swap_f=true;
  
 if (swap_f&&(!finish[i]))
 {
 strcpy(min_data,first_data[i]);
 index=i;
 }
 }
  
 fprintf(fp_out_file, "%s\n", min_data);
 if (fscanf(fp_array[index], "%s\n", first_data[index]) == EOF)
 finish[index] = true;
 }
  
 fclose(fp_out_file);
 delete []finish;
 for (i = 0; i < file_num; i++)
 fclose(fp_array[i]);
 delete [] fp_array;
 }
  
 int check_file()
 {
 FILE *fp_in_file = fopen("sortedfile.txt", "r");
 check_fp(fp_in_file);
 FILE *fp_out_file = fopen("result.txt", "w");
 check_fp(fp_out_file);
  
 char *space[memory_size]={NULL};
 int num = read_data(fp_in_file, space);
  
 char refd[30]={0};
 char moved[30];
 int count=1;
 int recyle=1;
 strcpy(refd,space[0]);
 int f=0;
 while(true)
 {
 if (recyle<num)
 {
 memset(moved,0,sizeof(moved));
 strcpy(moved,space[recyle]);
 }
 else{
 recyle=0;
 for(int i=0;i<num;i++)
 if (space[i] != NULL)
 {
 delete []space[i];
 space[i]=NULL;
 }
 num = read_data(fp_in_file, space);
 if (num == 0)
 break;
 else{
 memset(moved,0,sizeof(moved));
 strcpy(moved,space[recyle]);
 }
 }
 recyle++;
 if(!strcmp(refd,moved))
 {
 count++;
 f=1;
 }else{
 //printf("%s %d\n",refd,count);
 fprintf(fp_out_file,"%d-%s\n",count,refd);
 //fprintf(fp_out_file, "%s\n", min_data);
 count=1;
 memset(refd,0,sizeof(refd));
 strcpy(refd,moved);
 f=0;
 }
  
 }
 if(f)
 fprintf(fp_out_file,"%d-%s\n",count,refd);
 else
 fprintf(fp_out_file,"%d-%s\n",1,moved);
  
 fclose(fp_in_file);
 fclose(fp_out_file);
  
 return 0;
 }
 int main()
 {
  
 cout<<"1"<<endl;
 int aux_file_num = memory_sort("data.txt");
 cout<<"2"<<endl;
 merge_sort(aux_file_num,"sortedfile.txt");
 cout<<"3"<<endl;
 char files[1000][30]={0};
 int f_count=listFodler(files);
 remove_files(files,f_count);
 cout<<"4"<<endl;
  
 check_file();
  
 aux_file_num = memory_sort("result.txt");
 cout<<"5"<<endl;
 merge_sort(aux_file_num,"SortResult.txt");
 cout<<"6"<<endl;
 memset(files,0,sizeof(files));
 f_count=listFodler(files);
 remove_files(files,f_count);
 cout<<"7"<<endl;
 system("pause");
  
  
 return 0;
 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值