/** * 数据结构和算法-- C 描述 */ // 时间复杂度: c < logN < logN 的平方 < N < NlogN < N 的平方 < N 的立方 < 2 的 N 次方 // //----------------------------------------------------------------------------- /** * 找出 N 个数中最大的和(连续的数) */ int maxSum( const int A[], int N) { int i; int thisMax = 0; int sumMax = 0; for( i = 0; i < N; i++){ thisMax += A[i]; if( thisMax > sumMax){ sumMax = thisMax; }else if( thisMax < 0){ thisMax = 0; } } return sumMax; } 分析:首先简化问题 // ! important 这 N 个数可以总结为两种形式:(相邻的同号的合并) i. 正 负 正 负 正 负 .... ii. 负 正 负 正 负 正 .... 最大和序列的开始一定是正数 正数开始的序列相加的和(thisMax)有两种趋势: 1,增大:这时只需记录新的最大值即可 2,减小:这时候需要保留之前的最大值(sumMax),因此 i. 如果这个值(thisMax)小于 0,那么就从重新开始相加 ii. 之后如果和(thisMax)大于原来的 sumMax,就记录新的 sumMax //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- /** * 求一个数的 N 次幂(exponentiation) */ // 这个可能在大多数语言里都会提供一个函数 pow() 之类的,但是这里要说明的方法 long int POW( long int x, unsigned int N) { if( N == 0){ return 1; } if( N == 1){ return x; } if( N % 2 == 0){ return POW( x*x, N/2); }else{ return POW( x*x, N/2) * x; } } //说明:程序的时间复杂度为 logN 如果直接进行 N 个 x 相乘,那么时间复杂度为 N //----------------------------------------------------------------------------- ADT: Abstract Data Type. 抽象数据类型 ADT 是数据结构和作用于数据结构上的操作的的集合,这些操作是独立于实现的。 A set of data values and associated operations that are precisely specified independent of any particular implementation. // /** * 线性表 */ 1, A1, A2, A3, A4, ..., An. 2, 线性表的大小为 n 3, 线性表可以为空(empty list) 4, 操作:printList, makeEmpty, insert, delete, find, findKth // // 实现 1, 数组实现 i. 需要实现估计线性表的大小,这个估计要大于实际可能用到的大小,因此会浪费空间 ii. 线性表通常不用数组实现 // 2, 链表 i. 通常会单独设置个头节点(head node), 这个节点的数据域为空,可以把头节点作为 A0 ii. Linked List ADT 的头文件 ---- #ifndef _List_H struct Node; typedef struct Node *PtrToNode; typedef PtrToNode List; typedef PtrToNode Position; List makeEmpty( List L); int isEmpty( List L); int isLast( Position P, List L); Position find( ElementType x, List L); void delete( ElementType x, List L); Position findPrevious( ElementType x, List L); void insert( ElementType x, List L, Position p); void deleteList( List L); Position header( List L); Position first( List L); Position advance( Position p); ElementType retrive( Position p); #endif struct Node{ ElementType element; Position next; } ---- iii. 几个操作 ---- int isEmpty( List L) { return L->next == NULL; } /** * 说明: * 因为链表有头节点(head node),所以检查的是 L->next * 也就是说新建一个链表时也得分配一个头节点,总之就是头节点必须有的 * 也就是 L->next 才指向第一个节点 */ Position find( ElementType x, List L) { Position P; p = L->next; while( p != NULL && p->element != x) p = p->next return p; } /** * 说明: 函数返回有两种情况: * 1, P = NULL 结束的循环,此时已经到达链表结尾,没有找到返回 NULL * 2, p->element = x 结束的循环,找到返回节点指针 */ Position findPrevious( ElementType x, List L) { Position p; p = L; while( p->next != NULL && p->next->element != x) p = p->next return p; } /** * 说明: * 1, 只和 find 稍微不同 */ void delete( ElementType x, List L) { Position p, tmpNode; p = findPrevious( x, L); if( ! isLast( p, L) ){ /* 注意要检查 L 中是否存在该节点*/ tmpNode = p->next; p->next = tmpNode->next; free( tmpNode); } } ---- iv. 注意: 1, Whenever you do an indirection, you must make sure that the pointer is not NULL elem = p->element;/* 必须确保 p 不为 NULL */ 2, 链表的头文件中定义的都是指针类型,因此链表中所有节点,还有头节点都是使用 malloc() 函数分配的, 一般不会使用节点类型去声明一个节点,因此程序中用到的都是节点指针类型,一般不用节点类型 struct Node n; /* 这样的话还需要取 n 的地址赋给一个指针变量,其实这个 n 就没什么用了 */ // 线性表实现分类 顺序表-数组实现 链表-指针结构体-动态链表 双向链表 循环链表 静态链表-结构体数组实现 // 静态链表 -- typedef int PtrToNode; typedef PtrToNode List; typedef PtrToNode Position; struct Node{ ElementType element; Position next; }; #define SpaceSize 100 struct Node CursorSpace[ SpaceSize]; /* SpaceSize 为常量 */ -- 静态链表初始化 -- void initialize( void) { int i; for( i = 0; i < SpaceSize; i++){ if( i+1 == SpaceSize ) SpaceSize[i] = 0; else SpaceSize[i] = i + 1; } } /** * 说明: * 初始情况是:CursorSpace 所有元素都属于为分配空间链表,组成一个单向循环链表 */ static cursorAlloc( void) { Position p; if( ! isLast( CursorSpace[0].next ) ){ p = CursorSpace[0].next; CursorSpace[0].next = CursorSpace[ p].next; } return p; } // static void cursorFree( Position p) { CursorSpace[ p].next = CursorSpace[0].next; CursorSpace[0].next = p; } -- 说明: 1, 静态链表也需要事先分配一块够大的空间(这里是 CursorSpace 数组) 2, 静态链表实际上包括两个表,其中一个是未使用的空间 (未使用的空间组成一个单向循环链表,这个链表中总是有索引为 0 的数组元素,他相当于头节点, 这其实是一个栈,因为从链表中删除或向链表中插入节点都是从未分配空间的头节点的下元素个得到的) 3, 链表空间中可以有多个链表,这些链表的结尾均指向 0( CursonSpace[0] ) 4, 当 CursorSpace[0].next = 0 时,没有空间可以分配了 /** * 说明一下:没有留意 c 和 PHP 中函数定义的区别,刚才都在 c 中函数定义中加上了 function * c 和 java 是编译型语言, 是强类型语言,在定义函数时不需要加 function(也不能加) * PHP 和 JavaScript 是解析型语言,弱类型语言,定义函数时必须加 function 关键字 */ // /** * 栈 */ // 1, 栈是插入和删除都有一定限制的线性表,因此栈是特殊的线性表 2, 栈的也是线性表,因此任何实现线性表的方式都可以实现栈 3, 栈的结构体指针实现方式和链表完全一样,只不过操作的实现方式不同,因此下面之记录栈的数组实现方式 4, 在入栈操作前要检查栈是否已满,出栈前检查占是否为空 // 5, 数组实现 ---- #ifndef _Stack_H struct StackRecord; typedef struct StackRecord *Stack; int isEmpty( Stack S); int isFull( Stack S); Stack createStack( int maxSize); void disposeStack( Stack S); void makeEmpty( Stack S); void push( ElementType x, Stack S); ElementType top( Stack S); void pop( Stack S); ElementType topAndPop( Stack S); #endif #define MinStackSize 5 #define EmptyTOS -1 /* 栈空时栈定标志 */ struct StackRecord{ int capacity; int topOfStack; ElementType *array; } ---- Stack createStack( int maxSize) { Stack S; if( maxSize < MinStackSize){ error( "Stack size is too small"); } S = (Stack *)malloc( sizeof( struct StackRecord) ); if( S == NULL){ fatalError( "Out of space!!!"); } S->array = ( ElementType *)malloc( sizeof( ElementType) * maxSize); if( S->array == NULL){ fatalError("Out of space!"); } S->capacity = maxSize; makeEmpty( S); return S; } void makeEmpty( Stack S) { S->topOfStack = EmptyTOS; } void push( ElementType x, Stack S){ if( isFull( S) ) fatalError("Full Stack") else S->array[ ++s->topOfStack ] = x; } int isFull( Stack S) { return S->capacity == S->topOfStack; }