您的位置:首页 > 理论基础 > 数据结构算法

数据结构学习笔记1(算法分析)

2014-02-13 15:05 176 查看

一 最大子序列和问题

本来眼高手低想着书上的例子都给了伪代码并且自己理解了准备直接翻过例程,后来想着这么简单不如试试,咳咳,试了将近两个小时....

《数据结构与算法分析》P20,P21的算法实现

#include "stdio.h"
int Maxsum(const int a[],int N);
int MaxSUM(const int a[],int Left,int Right);
int Max3(int,int,int);

int main(void)
{
	 int a[8]={4,-3,5,-2,-1,2,6,-2};
	 int n=sizeof(a)/sizeof(int);
	 int Max=Maxsum(a,n);
	 int Max2=MaxSUM(a,0,n-1);//这里边界是n-1

	 printf("%d\n",Max);
	 printf("%d\n",Max2);
	 
	 
	 return 0;
}
int Maxsum(const int a[],int N)
{
	 int Max=0,ThisMax=0;
	 for (int i=0;i<N;i++)
	 {
		  ThisMax+=a[i];
		  if (ThisMax>Max)
		  {
			   Max=ThisMax;
		  }
	 }
	 return Max;
}
MaxSUM(const int a[],int Left,int Right)
{
	 int Maxleft,Maxright;
	 int Borderleft,Borderright,ThisMax;
	 int n,i;

	 if (Left==Right)
	 {
		  if(a[Left]>0)
			   return(a[Left]);
		  else 
			   return 0;
	 }
	 n=(Left+Right)/2;
	 Maxleft=MaxSUM(a,Left,n);
	 Maxright=MaxSUM(a,n+1,Right);

	 ThisMax=Borderleft=0;
	 //for (i=Left;i<=n;i++)//这里出错
	 for(i=n;i>=Left;i--)
	 {
		  Borderleft+=a[i];
		  if (Borderleft>ThisMax)
		  {
			   ThisMax=Borderleft;
		  }
	 }
	 Borderleft=ThisMax;

	 ThisMax=Borderright=0;
	 for (i=n+1;i<=Right;i++)//出错
	 {
		  Borderright+=a[i];
		  if (Borderright>ThisMax)
		  {
			   ThisMax=Borderright;
		  }
	 }
	 Borderright=ThisMax;

	 //return Max3(Borderleft,Borderright,Maxleft+Maxright);
	 return Max3(Borderleft+Borderright,Maxleft,Maxright);
}
int Max3(int a,int b,int c)
{
	 int temp;
	 temp=(a>b?a:b);
	 temp=(temp>c?temp:c);
	 return temp;
}


调试过程中出错如下:

1MaxSUM计算边界(a,0,n-1)习惯性写为n,这里是对a数组进行访问,下标界限为n-1

2计算Borderleft这里是先二分计算左边四个数的最大和Maxleft和右边的Maxright,Border是一定要有a
这个数,所以应该从n开始自减

3计算Borderright的i应该从n+1开始

算法分析:

第一种方法是联机算法,只对数据进行一次扫描,一旦a[i]被读入并被处理就不再需要被记忆。若数组在磁盘或磁带上,就可以顺序读入,主存中不必存储数组的任何部分。这里其实作者思维比较好理解:通过Max只记录某个区段的最大和

第二种方法时间复杂度为O(NlogN),有点二分的意思,比较好理解。但是如果深思,发现

Maxleft=MaxSUM(a,Left,n);

Maxright=MaxSUM(a,n+1,Right);

这里的递归调用,如果把整个程序的时间复杂度表示为T(N),这里就是T(N/2)。若a数组有32个元素,则要计算左边16个的Maxleft和右边16个Maxright、以及分别包含第16、17元素的Borderleft和Borderright。以计算Maxleft为例,调用MaxSUM(a[32],0,15),需要再分别计算16个元素的Maxleft、Maxright、Borderleft+Borderright;这里面又包含了左8个右8个元素的递归....以此类推。

所以书上也说对于递归调用不必一步一步分析清楚每一步的具体计算,只需要满足四条基本法则即可。易知,上述递归调用中没有做重复性工作。

二 时间复杂度分析中的对数问题

法则:如果一个算法用常数时间O(1)将问题大小削减为其中一部分(常为1/2),则该算法就是O(logN)

常包含在对分查找、欧几里得算法、幂运算中

对分查找代码:

int Low=0,High=n-1;

if(Low==High)
	return A[Low];
while(Low<High)
{
	Mid=(Low+High)/2;
	
	if (A[Mid]>X)
		High=Mid;
	else if (A[Mid]<X)
		Low=Mid;
	else 
		return Mid;
}

对分查找一次比较(调试没问题,不知道原理正确不)

#include "stdio.h"

int main(void)
{
	 int a[]={2,4,5,7,8,9,11};
	 int b[]={2,4};

	 int n1=sizeof(a)/sizeof(int);
	 int n2=sizeof(b)/sizeof(int);

	 int low=0,high=n1-1,mid,x=4;
	 while (low<=high)
	 {
		  mid=(low+high)/2;
		  if (x<=b[mid])
		  {
			   high=mid;
		  }
		  else low=mid+1;

		  if (high==low)
		  {
			   break;
		  }
	 }

	 if (a[high]==x)
	 {
		  printf("%d\n",high);
	 }
	 else printf("0\n");

	 return 0;
}


欧几里得算法(用于求最大公约数)

unsigned int tmp;
while(N>0)
{
         tmp=M%N;
         M=N;
         N=tmp;
}
return M;


幂运算 就是计算X的N次方,利用递归每次计算X的N/2次方

3


埃拉托斯特尼筛法


(这个gif的图片不能在csdn上显示?)

eratosthenes筛是一种用于计算小于N的所有素数的方法,如图的表中,先找出最小的整数i,打印出来,删除i,2i,3i,...直到N;然后i递增至i>N^(1/2)结束,将剩下的没有删除的数打印出来,算法终止。算法实现起来很简单,具体原理可参见http://zh.wikipedia.org/zh-cn/%E5%9F%83%E6%8B%89%E6%89%98%E6%96%AF%E7%89%B9%E5%B0%BC%E7%AD%9B%E6%B3%95

该算法时间复杂度为O(N*loglogN),问题是:怎么算的?

三 课后习题选解

2.19

算法的基本思想是基于以下基础:如果N是偶数,那么只有元素a的个数大于N/2+1才能成为主要元素,即不可能a?a?……这么重复下去出现a是主要元素的结果,算法思想也就有了基础;如果N是奇数,那么必然主要元素个数>=(N/2+1),这种情况下首先对前N-1个数进行处理,如果B中有candidate,那么A中最后一个元素不影响B的结果,如果B中没有candidate那么可以把第N个元素放入数组B中。

在递归过程中,B里面只有2个或者少于2个的元素时递归结束。这是为了避免出现两个元素个数相等的情况,但是也只能出现这种特殊情况。否则,B中有一个元素,只需进行遍历比较;B中没有元素则返回。

易知每次递归循环数组长度减半,则T(N)=T(N/2)+O(N)所以递归的时间复杂度是O(N*logN)。后面的遍历比较时间复杂度为O(N),又因为是顺序执行,所以程序在不考虑输入情况下时间复杂度为O(N*logN)。

程序如下:

问题list:

1 int *A1后即使A1=(int *)malloc(sizeof(A)); A1还是一个指针,只不过能当做数组来用。但是在调试过程中单看A1,值就是A1[0];但是A1[3]也有意义。

2 sizeof(A1)就是A1的大小,A1是指针,大小就是其指针类型所占字节数

3 memcpy()第三个参数大小应该是复制的字节数,不是int型的个数(在这里栽了大跟头!!!)。详细内容参考/article/1441612.html

#include "stdio.h"
#include "string.h"
#include "stdlib.h"

int MajorEle(int A[],int n);
int main(void)
{
	 int A[]={3,3,4,2,4,4,2,4,4,5};
	 int *A1;
	 int m,n,j,k,i,a,b;

	 n=sizeof(A)/sizeof(int);
	 A1=(int*)calloc(n,sizeof(int));
	 memcpy(A1,A,n*sizeof(int));
	 m=MajorEle(A,n);

	 if (m==0)	 
		  printf("There is no major element.\n");
	 else if (m==1)
	 {
		  j=A[0];
		  k=0;
		  for (i=0;i<n;i++)
		  {
			   if(j==A1[i]) k++;
		  }
		  if(k>=n/2+1)
			   printf("The major element is %d.\n",j);
		  else
			   printf("There is no major element.\n");
	 }
	 else if(m==2)
	 {
		  a=b=0;
		  j=A[0];
		  k=A[1];
		  for (i=0;i<n;i++)
		  {
			   if(j==A1[i]) a++;
			   if(k==A1[i]) b++;
		  }
		  if(a>=n/2+1) 
			   printf("The major element is %d.\n",j);
		  else 
			   if(b>=n/2+1) 
					printf("The major element is %d.\n",k);
		  else 
			   printf("There is no major element.\n");
	 }

	 free(A1);
	 return 0;
}
int MajorEle(int A[],int n)
{	 
	 int i,m=0;
	 bool flag=false;
	 int *B;

	 if (n%2==0)
	 {
		  B=(int *)malloc(n/2*sizeof(int));
		  for (i=0;i<n;i=i+2)
		  	   if (A[i]==A[i+1])
			   {
					B[m]=A[i];
					m++;
			   }		  
	 } 
	 else
	 {
		  B=(int *)calloc((n/2+1),sizeof(int));
		  printf("B_space=%d\n",sizeof(B));
		  for (i=0;i<n;i=i+2)
		  {
			   if (A[i]==A[i+1])
			   {
					B[m]=A[i];
					m++;
					flag=true;
			   }
		  }
		  if (flag==false)
		  {
			   B[m]=A[n-1];
			   m++;//这里如果没有m++则下面赋值不成功
		  }		  
	 }
	 
	 memcpy(A,B,m*sizeof(int));//这里应该是字节数

	 for (i=0;i<m;i++)
	 {
		  printf("%d\n",A[i]);
	 }

	 if (m<=1)
	 {
		  free (B);
		  return m;
	 } 
	 else
	 {
		  n=m;
		  MajorEle(A,n);		  
	 }
}
话说这么简单的程序我竟然写了将近两个半小时!!!太水,基础概念掌握不牢固。平时贪玩,有些基础知识应该回顾的。

2.7.3 http://dl.acm.org/citation.cfm?id=315746&bnc=1页面的pdf文件可以作为参考
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: