星海's Blog

老头初学编程

子序列问题

#include <stdlib.h>
#include <stdio.h>
/*
 * ===  FUNCTION  ======================================================================
 *         Name:  maxsubsum
 *  Description:  用分治法找出数组中的最长公共子序列
 * =====================================================================================
 */

int max3(int a, int b, int c)
{
    int max;
    if (a > b) {
        max = a;
    } else {
        max = b;
    }
    return max > c ? max : c;
}
int maxsubsum(const int a[], int left, int right)
{
    int maxleft, maxright;                      /* 左右最大子序列和 */
    int maxleftborder = 0, maxrightborder = 0;  /* 包含中间元素的左右两部份的和 */
    int leftbordersum = 0;
    int rightbordersum = 0;
    int mid, i;

    if (left == right) {
        if (a[left] > 0) {
            return left;
        } else {
            return 0;
        }
    }

    mid = (left + right) / 2;
    maxleft = maxsubsum(a, left, mid);
    maxright = maxsubsum(a, mid + 1 , right);

    for ( i = mid; i >= left; i--) {
        leftbordersum += a[i];
        if (leftbordersum > maxleftborder) {
            maxleftborder = leftbordersum;
        }
    }
    for ( i = mid + 1; i <= right; i++) {
        rightbordersum += a[i];
        if (rightbordersum > maxrightborder) {
            maxrightborder = rightbordersum;
        }
    }

    return max3(maxleft, maxright, maxleftborder + maxrightborder);
}

int main(void)
{
    int a[] = {4, -3, 5, -2, -1, 2, 6, -2};
    printf("%d\n", maxsubsum(a, 0, 7));
    return 0;
}
/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  maxsubmulti
 *  Description:  采用分治法求解最大子序列乘积。
 *  因为负负得正,所以设置了 minleftborder,minrightborder
 * =====================================================================================
 */
#include <stdio.h>
double max4(double, double, double, double);
double maxone(const double [], int);            /* 如果maxsubmulti返回结果为1.0,则可能是都为小于1的小数且至多有一个负元素 */
double maxsubmulti(const double [], int, int);

double maxsub(const double a[], int n)
{
    double maxsubm = maxsubmulti(a, 0, n-1);
    if (maxsubm == 1.0)
        return (maxone(a, n));
    else
        return maxsubm;
}
double maxsubmulti(const double a[], int left, int right)
{
    double maxleft, maxright;
    double maxleftborder = 1, maxrightborder = 1;
    double minleftborder = 1, minrightborder = 1;
    double leftborder = 1, rightborder =1;
    int mid, i;

    if (left == right) {
        if (a[left] > 0)
            return a[left];
        else
            return 0;
    }

    mid = (left + right) / 2;
    maxleft = maxsubmulti(a, left, mid);
    maxright = maxsubmulti(a, mid + 1, right);

    for (i = mid; i >= left; i--) {
        leftborder *= a[i];
        if (leftborder > maxleftborder)
            maxleftborder = leftborder;
        if (leftborder < minleftborder)
            minleftborder = leftborder;
    }

    for (i = mid + 1; i <= right; i++) {
        rightborder *= a[i];
        if (rightborder > maxrightborder)
            maxrightborder = rightborder;
        if (rightborder < minrightborder)
            minrightborder = rightborder;
    }

    return max4(maxleft, maxright, maxleftborder * maxrightborder, minleftborder * minrightborder);
}

int main(void)
{
    double arr[6] = {-1.0, 1000.0, 0.001, 1.5, -80, 10};
    printf("%f\n",maxsub(arr, 6));
    double arb[6] = {-3, 0.02, 0.001, 0.3, 0.11, 0.210};
    printf("%f\n",maxsub(arb, 6));
    return 0;
}

double max4(double a, double b, double c, double d)
{
    double max = a;
    if (max < b)
        max = b;
    
    if (max < c)
        max = c;
    if (max < d)
        max = d;
    return max;
}

double maxone(const double a[], int n)
{
    double maxd = a[0];
    int i;
    for (i = 1; i < n; i++)
        if (a[i] > maxd)
            maxd = a[i];
    return maxd;
}
#include <stdio.h>
#include <stdlib.h>
 
// max1num返回数组中最大值
int max1num(int a[], int n)
{
    int i,max;
    max = a[0];
    for (i = 1; i < n; i++)
        if (a[i] > max)
            max = a[i];
    return max;
}
 
//求数组a的最大子序列和
int maxsub(int a[], int n)
{
    int i,maxsum,linesum;
 
    maxsum = max1num(a, n);
 
    if (maxsum <= 0)
        return maxsum;
 
    linesum = 0;
    for (i = 0; i < n; i++) {
        linesum += a[i];
 
        if (linesum < 0)
            linesum = 0;
        if (linesum > maxsum)
            maxsum = linesum;
    }
    return maxsum;
}
 
 
// min1num返回数组中最小值
int min1num(int a[], int n)
{
    int i, min;
    min = a[0];
    for (i = 1; i < n; i++)
        if (a[i] < min)
            min = a[i];
    return min;
}
 
//求数组a的最小子序列和
int minsub(int a[], int n)
{
    int i;
    int minsum, linesum;
 
    minsum = min1num(a,n);
 
    if (minsum >= 0)    //如果minsum >= 0的话,数组中没有负数,minsum即是最小子序列(只有一个元素minsum)
        return minsum;
 
    linesum = 0;
    for (i = 0; i < n; i++) {
        linesum += a[i];
        if (linesum > 0 && linesum > minsum)
            linesum = 0;
        if (linesum < minsum)
            minsum = linesum;
    }
    return minsum;
}
 
int main(void)
{
    int a[]={-324,23,23,111,23232,222,1111,555,-222,22,-88888};
    printf("%d\n",minsub(a,11));
 
    int b[]={324,23,-300,111,-121,2,-456,555,222,22,88888};
    printf("%d\n",minsub(b,11));
 
    int c[]={-324,23,23,111,23232,222,1111,555,-222,22,-88888};
    printf("%d\n",maxsub(c,11));
 
    int d[]={324,23,-300,111,-121,2,-456,555,222,22,88888};
    printf("%d\n",maxsub(d,11));
 
 
    return 0;
}

sasf

数据结构与算法分析数学和文本部份

数据结构与算法分析 第一章 TEX PDF笔记


一,归纳法证明:
1,证明基准情形(base case)。就是确定定理对于某个小的(通常是退化的)值的正确性。
2,归纳假设(inductive hypothesis)。假设定理对直到某个有限数k的所有情况都是成立的,然后使用这个假设证明定理对下一个值(k + 1)也是成立的。

二,递归:
1,基准情形(base case)。
2,不断推进(make progress)。对于需要递归求解的情形,递归调用必须总能够朝着产生基准情形的方向推进。
3,设计法则(design rule)。假设所有的递归调用都能运行。
4,合成效益法则(compound interest rule)。在求解一个问题的同一实例时,切勿在不同的递归调用中做重复性的工作。

 

三,

O(N)的一些排序算法:

基数排序(radix sort),也称卡式排序(card sort)

桶式排序(bucket sort),该排序运行时间为O(P(N+B)),P是排序趟数,N是元素个数,B是桶数。

C素数筛法(更新中)

#include 	<stdio.h>
#include	<stdlib.h>
#include	<math.h>
#include	<stdbool.h>
#include	<time.h>

unsigned int prime(unsigned int max)
{
	unsigned int count = 0;	/* 返回count: 素数个数 */
	unsigned int i, j, k;

// 申请内存
	bool *sieve = (bool *) malloc(sizeof(bool) * (max));
	if (sieve == NULL) {
		fprintf(stderr, "\ndynamic memory allocation failed\n");
		exit(EXIT_FAILURE);
	}

	sieve[0] = false;
	sieve[1] = false;
	sieve[2] = true;

//除2之外,所有偶数都是合数
	for (i = 3; i < max; i++)
		if (i % 2)
			sieve[i] = true;
		else
			sieve[i] = false;

	k = (int)sqrt(max);

	for (i = 2; i < k; i++) {
		if (sieve[i])
			for (j = i + i; j < max; j += i) {
				sieve[j] = false;
			}
	}

	for (i = 0; i < max; i++)
		if (sieve[i])
			count++;

	free(sieve);
	sieve = NULL;

	return count;
}

int main(void)
{
	unsigned int count;
	unsigned int N = 100000000;
	clock_t start, end;

	start = clock();
	count = prime(N);

	end = clock();
	printf("[%u]以内素数个数%u 计算用时:%g 秒\n", N, count,
	       (double)(end - start) / (double)CLOCKS_PER_SEC);
}

[100000000]以内素数个数5761455 计算用时:5.86 秒

#include    <stdio.h>
#include    <stdlib.h>
#include    <math.h>
#include    <stdbool.h>
#include    <time.h>

/*
 * ===  FUNCTION  ======================================================================
 *         Name:  prime
 *  Description:  只存奇数的bool型数组,素数筛法。[0]表示自然数3
 * =====================================================================================
 */
unsigned int prime(unsigned int max)
{
    unsigned int count = 1;    /* 返回count: 素数个数,已经包括2,所以count为1 */
    unsigned int limit = max / 2 - 1;    /* max以内的素数用到的数组需要空间 max/2-1 */
    unsigned int i, k;
    unsigned int multiprime;

// 申请内存,       
    bool *sieve = (bool *) malloc(sizeof(bool) * (limit));
    if (sieve == NULL) {
        fprintf(stderr, "\ndynamic memory allocation failed\n");
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < limit; i++)
        sieve[i] = true;

// 2*i+3 为数组下标内容所代表的数值

    k = (unsigned int)sqrt(max);
    for (i = 0; 2 * i + 3 < k; i++) {
        if (sieve[i]) {
            int temp = 2 * i + 3;    /* temp代表数组下标所代表的真实数值 */
            /* 索引i所代表数值的奇数倍索引,3倍时为3*temp */
            for (multiprime = temp * 3; multiprime < max;
                 multiprime += 2 * temp)
                sieve[(multiprime - 3) / 2] = false;
        }

    }

    for (i = 0; i < limit; i++)
        if (sieve[i])
            count++;

    free(sieve);
    sieve = NULL;

    return count;
}

int main(void)
{
    unsigned int count;
    unsigned int N = 100000000;
    clock_t start, end;

    start = clock();
    count = prime(N);

    end = clock();
    printf("[%u]以内素数个数%u 计算用时:%g 秒\n", N, count,
           (double)(end - start) / (double)CLOCKS_PER_SEC);
}

[100000000]以内素数个数5761455 计算用时:2.85 秒

#include 	<stdio.h>
#include	<stdlib.h>
#include	<math.h>
#include	<stdbool.h>
#include	<time.h>

unsigned int prime(unsigned int max)
{
	unsigned int count = 0;	/* 返回count: 素数个数 */
	unsigned int i, j, k;

// 申请内存
	bool *sieve = (bool *) malloc(sizeof(bool) * (max));
	if (sieve == NULL) {
		fprintf(stderr, "\ndynamic memory allocation failed\n");
		exit(EXIT_FAILURE);
	}

	sieve[0] = false;
	sieve[1] = false;
	sieve[2] = true;

//除2之外,所有偶数都是合数
	for (i = 3; i < max; i++)
		if (i % 2)
			sieve[i] = true;
		else
			sieve[i] = false;

	k = (int)sqrt(max);

	/*------------------------------------------------------------------
	 *  台湾ACMTino同学的算法:i是素数,则下一个起点是i*i,把后面所有的i*i+2*x*i筛掉
         *  我的实现还不完备
	 *----------------------------------------------------------------*/
	for (i = 2; i < k; i++) {
		if (sieve[i])
			for (j = i * i; j < max; j += 2 * i) {
				sieve[j] = false;
			}
	}

	for (i = 0; i < max; i++)
		if (sieve[i])
			count++;

	free(sieve);
	sieve = NULL;

	return count;
}

int main(void)
{
	unsigned int count;
	unsigned int N = 100000000;
	clock_t start, end;

	start = clock();
	count = prime(N);

	end = clock();
	printf("[%u]以内素数个数%u 计算用时:%g 秒\n", N, count,
	       (double)(end - start) / (double)CLOCKS_PER_SEC);
}

[100000000]以内素数个数5761455 计算用时:3.53 秒

#include     <stdio.h>
#include    <stdlib.h>
#include    <math.h>
#include    <string.h>
#include    <time.h>
#include    <stdbool.h>

/*
 * ===  FUNCTION  ======================================================================
 *  Description:  只计算奇数数组里的奇数是否为素数,[0]为3
 *          prime(n)中的n为n以内的素数,不包括n本身。
 * =====================================================================================
 */
int prime(unsigned int n)
{
    unsigned int i, j, k;
    unsigned int limit = n / 2 - 1;

    unsigned int count = 1;    /* 数组中不包含的2也为素数 */
    bool *sieve = (bool *) malloc(sizeof(bool) * limit);
    if (sieve == NULL) {
        fprintf(stderr, "\ndynamic memory allocation failed\n");
        exit(EXIT_FAILURE);
    }
    for (i = 0; i < limit; i++)
        sieve[i] = true;

    j = (unsigned int)sqrt(n);
    for (i = 0; 2 * i + 3 <= j; i++)
        if (sieve[i]) {
            int temp = 2 * i + 3;    /* 2*i+3为数组下标实际代表的数值 */
            for (k = temp * temp; k <= n; k += 2 * temp)    /* x是素数,则下一个起点是x*x,并把后面所有的x*x+2*x*i筛掉 */
                sieve[(k - 3) / 2] = false;
        }
    for (i = 0; i < limit; i++)
        if (sieve[i])
            count++;

    free(sieve);
    sieve = NULL;

    return count;

}

int main(int argc, char *argv[])
{
    unsigned int count;
    unsigned int N = 100000001;
    clock_t start, end;

    start = clock();
    count = prime(N);

    end = clock();
    printf("[%u]以内素数个数%u 计算用时:%g 秒\n", N, count,
           (double)(end - start) / (double)CLOCKS_PER_SEC);
    return 0;
}

[100000000]以内素数个数5761455 计算用时:2.56 秒

/*-----------------------------------------------------------------------------
 *  可参考论文COMPUTING π(x): THE MEISSEL-LEHMER METHOD
 *  以下函数为百度C语言贴吧 5230所做 (我完全不懂撒 -__-)
 *-----------------------------------------------------------------------------*/
#include	<stdio.h>
#include	<string.h>
#include	<stdlib.h>
#include	<time.h>
#include	<math.h>

int *primarr, *v;
int q = 1, p = 1;

//π(n)
int pi(int n, int primarr[], int len)
{
	int i = 0, mark = 0;
	for (i = len - 1; i > 0; i--) {
		if (primarr[i] < n) {
			mark = 1;
			break;
		}
	}
	if (mark)
		return i + 1;
	return 0;
}

//Φ(x,a)
int phi(int x, int a, int m)
{
	if (a == m)
		return (x / q) * p + v[x % q];
	if (x < primarr[a - 1])
		return 1;
	return phi(x, a - 1, m) - phi(x / primarr[a - 1], a - 1, m);
}

int prime(int n)
{
	char *mark;
	int mark_len;
	int count = 0;
	int i, j, m = 7;
	int sum = 0, s = 0;
	int len, len2, len3;

	mark_len = (n < 10000) ? 10002 : ((int)exp(2.0 / 3 * log(n)) + 1);

	//筛选n^(2/3)或n内的素数
	mark = (char *)malloc(sizeof(char) * mark_len);
	memset(mark, 0, sizeof(char) * mark_len);
	for (i = 2; i < (int)sqrt(mark_len); i++) {
		if (mark[i])
			continue;
		for (j = i + i; j < mark_len; j += i)
			mark[j] = 1;
	}
	mark[0] = mark[1] = 1;

	//统计素数数目
	for (i = 0; i < mark_len; i++)
		if (!mark[i])
			count++;

	//保存素数
	primarr = (int *)malloc(sizeof(int) * count);
	j = 0;
	for (i = 0; i < mark_len; i++)
		if (!mark[i])
			primarr[j++] = i;

	if (n < 10000)
		return pi(n, primarr, count);

	//n^(1/3)内的素数数目
	len = pi((int)exp(1.0 / 3 * log(n)), primarr, count);
	//n^(1/2)内的素数数目
	len2 = pi((int)sqrt(n), primarr, count);
	//n^(2/3)内的素数数目
	len3 = pi(mark_len - 1, primarr, count);

	//乘积个数
	j = mark_len - 2;
	for (i = (int)exp(1.0 / 3 * log(n)); i <= (int)sqrt(n); i++) {
		if (!mark[i]) {
			while (i * j > n) {
				if (!mark[j])
					s++;
				j--;
			}
			sum += s;
		}
	}
	free(mark);
	sum = (len2 - len) * len3 - sum;
	sum += (len * (len - 1) - len2 * (len2 - 1)) / 2;

	//欧拉函数
	if (m > len)
		m = len;
	for (i = 0; i < m; i++) {
		q *= primarr[i];
		p *= primarr[i] - 1;
	}
	v = (int *)malloc(sizeof(int) * q);
	for (i = 0; i < q; i++)
		v[i] = i;
	for (i = 0; i < m; i++)
		for (j = q - 1; j >= 0; j--)
			v[j] -= v[j / primarr[i]];

	sum = phi(n, len, m) - sum + len - 1;
	free(primarr);
	free(v);
	return sum;
}

int main()
{
	int n;
	int count;
	clock_t start, end;
	scanf("%d", &n);

	start = clock();
	count = prime(n);
	end = clock() - start;
	printf
	    ("[%d]内的素数个数为%d,计算用时:%d毫秒\n",
	     n, count, (int)end / 1000);
	getchar();
	return 0;
}

 

入门排序算法(逐步更新)

/*交换排序的基本思想是:两两比较待排序记录的关键字,发现两个记录的次序相反时即进行交换,直到没有反序的记录为止。 
应用交换排序基本思想的主要排序方法有:冒泡排序和快速排序。*/

/*  冒泡排序
 *   */

#include <stdio.h>

void bubble_sort(int *x, int n)
{
	int i, j, exchange, temp;	//用exchange保存交换标志
	for (i = n - 1; i > 0; i--) {	//冒泡算法最多做n-1趟排序
		exchange = 0;	//本趟排序开始前,交换标志应为假
		for (j = 0; j < i; j++)	//预置k=0,循环扫描后更新
			if (*(x + j) > *(x + j + 1)) {
				temp = *(x + j);
				*(x + j) = *(x + j + 1);
				*(x + j + 1) = temp;
				exchange = 1;	//发生交换,置标志为1
			}
		if (!exchange)	//如果未发生交换,退出
			return;
	}  
}

int main(void)
{
	int i;
	int a[5] = { 5, 3, 2, -5, 1 };
	bubble_sort(a, 5);
	for (i = 0; i < 5; i++)
		printf("%d ", a[i]);
	return 0;
}
#include <stdio.h>

void select_sort(int a[], int n)
{
	int i, j, temp, iMin;
	for (i = 0; i < n - 1; ++i) {
		iMin = i;
		for (j = i + 1; j < n; ++j)
			if (a[j] < a[iMin])
				iMin = j;
		if (iMin != i)
			temp = a[i], a[i] = a[iMin], a[iMin] = temp;
	}
}
static void merge(int array[], int low, int mid, int high)
{
	int i, k;
	int *temp = (int *) malloc((high-low+1) * sizeof(int)); //申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
	int begin1 = low;
	int end1 = mid;
	int begin2 = mid + 1;
	int end2 = high;
 
	for (k = 0; begin1 <= end1 && begin2 <= end2; ++k)  //比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
		if(array[begin1]<=array[begin2])
			temp[k] = array[begin1++];
		else
			temp[k] = array[begin2++];	
	if(begin1 <= end1) //若第一个序列有剩余,直接拷贝出来粘到合并序列尾
		memcpy(temp+k, array+begin1, (end1-begin1+1)*sizeof(int));
	if(begin2 <= end2) //若第二个序列有剩余,直接拷贝出来粘到合并序列尾
		memcpy(temp+k, array+begin2, (end2-begin2+1)*sizeof(int));
        memcpy(array+low, temp, (high-low+1)*sizeof(int));//将排序好的序列拷贝回数组中
	free(temp);
}


void merge_sort(int array[], unsigned int first, unsigned int last)
{
	int mid = 0;
	if(first<last)
	{
		mid = (first+last)/2;
		merge_sort(array, first, mid);
		merge_sort(array, mid+1,last);
		merge(array,first,mid,last);
	}
}
/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  SHELLSORT
 *  Description:  Donald Shell 最初建议步长选择为n/2并且对步长取半直到步长达到1。虽然这样取可以比O(n2)类的算法(插入排序)更好,但这样仍然有减少平均时间和最差时间的余地。 可能希尔排序最重要的地方在于当用较小步长排序后,以前用的较大步长仍然是有序的。比如,如果一个数列以步长5进行了排序然后再以步长3进行排序,那么该数列不仅是以步长3有序,而且是以步长5有序。如果不是这样,那么算法在迭代过程中会打乱以前的顺序,那就不会以如此短的时间完成排序了。
步长序列 	最坏情况下复杂度
n / 2i 	\mathcal{O}(n2)
2k − 1 	\mathcal{O}(n3 / 2)
2i3i 	\mathcal{O}(nlog2n)

已知的最好步长序列由Marcin Ciura设计(1,4,10,23,57,132,301,701,1750,…) 这项研究也表明“比较在希尔排序中是最主要的操作,而不是交换。”用这样步长序列的希尔排序比插入排序和堆排序都要快,甚至在小数组中比快速排序还快,但是在涉及大量数据时希尔排序还是比快速排序慢。

另一个在大数组中表现优异的步长序列是(斐波那契数列除去0和1将剩余的数以黄金分割比的两倍的幂进行运算得到的数列):(1, 9, 34, 182, 836, 4025, 19001, 90358, 428481, 2034035, 9651787, 45806244, 217378076, 1031612713, …)[2]
 * =====================================================================================
 */
#include 	<stdio.h>
#include	<stdlib.h>

void shellsort(int data[], int n)
{
	int gap, i, j, temp;

	for (gap = n / 2; gap > 0; gap /= 2)
		for (i = gap; i < n; i++)
			for (j = i - gap; j >= 0 && data[j] > data[j + gap];
			     j -= gap) {
				temp = data[j];
				data[j] = data[j + gap];
				data[j + gap] = temp;
			}
}

int main(void)
{
	int a[] = { 53, 325, 456, 24352, 2345, 32, 234, 23, 21, 2, 3, 4325 };
	shellsort(a, 12);
	for (int i = 0; i < 12; i++)
		printf("%d\t", a[i]);
	return EXIT_SUCCESS;
}
#include 	<stdio.h>
#include	<stdlib.h>

void swap(int v[], int i, int j)
{
	int temp = v[i];
	v[i] = v[j];
	v[j] = temp;
}

void qsort1(int v[], int left, int right)
{
	int i, last;
	if (left >= right)
		return;
	swap(v, left, (left + right) / 2);
	last = left;
	for (i = left + 1; i <= right; i++)
		if (v[i] < v[left])
			swap(v, ++last, i);
	swap(v, left, last);
	qsort1(v, left, last - 1);
	qsort1(v, last + 1, right);
}
void InsertSort(char array[], unsigned int n)
{
	int i, j;
	int temp;
	for (i = 1; i < n; i++) {
		temp = array[i];	//store the original sorted array in temp
		for (j = i; j > 0 && temp < array[j - 1]; j--)	//compare the new array with temp(maybe -1?)
		{
			array[j] = array[j - 1];	//all larger elements are moved one pot to the right
			array[j - 1] = temp;
		}
	}
}

 

算法相关的网址

http://zh.wikipedia.org/zh-cn/Category:%E7%AE%97%E6%B3%95

 

http://en.wikipedia.org/wiki/Category:Algorithms