第5章 数组和广义表
数组定义:存储在连续内存单元的线性表,是一种随机存储结构。多维数组可以看作每个数据元素都是一个一维数组。
C/C++/PASCAL/BASIC等语言是行序存储,FORTRAN等是列序存储。
行序存储的数组a[m][n]: loc(a[i][j]) = loc(a[0][0]) + (i*n + j) * k --- k是每个元素所占的存储单元
特殊矩阵的压缩存储:
对称矩阵(元素关于主对角线对称,ai,j = aj,i),
三角矩阵(矩阵的上(下)三角中的元素均为常数,不包括对角线),
对角矩阵(所有非0元素集中在以对角线为中心的带状区域内)
把这些具有一定分布规律的元素压缩存储到一个空间中,进行一定的映射。
稀疏矩阵:只有当非0元素个数远小于总个数时,只存储非0元素;
1。 下标按照行有序的三元组顺序表存储结构
2。 十字链表,像编织一样的串起来。每一行/列均设置一个链表,方便行列搜索,降低复杂度。
//三元组 typedef struct { int r; //row int c; //column int d; //data }TupNode; typedef struct { int rows; int cols; int nums; //number of data TupNode data[MaxSize]; }TsMatrix;
//十字链表 typedef struct mtxn { int row; int col; //行号和列号 struct mtxn* right, * down; union { int data; struct mtxn* link; // 链表头节点指向列头节点链表的第一个行节点 }tag; }
广义表:
数据元素相对有序,长度为最外层包含元素的个数,深度为所包含括弧的重数。可以递归,因为每个元素可以是原子数据也可以是子表数据。
采用动态链式结构,添加一个tag域指示是否为子表。
//广义表 typedef struct lnode { int tag; // =0 标识原子数据 union { int data; struct lnode* sublist; }val; struct lnode* link; }
第6章 递归
在定义一个过程或函数时出现调用本过程或本函数的成分,成为递归。
数据的定义是递归的,数据结构是递归的,问题的求解方法是递归的。
其执行过程分为分解和求值,是函数嵌套调用的特殊情况,采用代码共享的方式,每次调用同一个函数的代码,每一次调用开辟栈空间,存放返回地址和参数,全部执行完后,栈应该为空。
#include <stdio.h> #include <stdlib.h> //recursive /* 递归是一种分而治之的方法: 1.分析原问题,假设子问题 2.假设子问题可解,确定原问题的解 3.确定一个特殊情况,作为递归出口 */ //八皇后问题,皇后放在不同行,列,对角线 //place(k,n) 标识在1~k-1列上放好了,place(k+1,n),标志在1~k列上放好了,是子问题 int cont =0; int q[20]; void print(int n) { int i; cont++; printf("第%d个解:",cont); for(i = 1; i<= n; i++) { printf("%d",q[i]); } printf("\n"); } int find(int i,int k) { int j; j = 1; while(j < k) { if(q[j] == i || abs(q[j] - i) == abs(j-k)) { return 0; } j ++; } return 1; } void place(int k,int n) { if(k > n) { print(n); } else { for(int i = 1; i<= n; i++) { //能放就放 if(find(i,k)) { q[k] = i; place(k+1,n); } } } } int main() { place(1,4); return 0; }
//哈夫曼树和并查集 //求最小带权路径长度,用于简化编码 //判断两个元素之间的关系,利用集合合并,用代表元素标识集合的方法,不断合并子树,快速找出两个元素是否属于同一个集合 #include <stdio.h> #include <stdlib.h> typedef struct{ char data; double weight; int parent; int lchild; int rchild; }HTNode; //n个叶子节点,n-1个非叶子节点,更新每个非叶子节点的值 void CreatHT(HTNode ht[],int n) { int i,j,k,lnode,rnode; double min1,min2; for(int i=0;i<2*n-1;i++) { ht[i].parent = ht[i].lchild = ht[i].rchild = -1; } //构造哈夫曼树,lnode,rnode是最小权重的两个节点位置 for(i = n; i<2*n-1; i++) { min1 = min2 = 32767; lnode = rnode = -1; for(k=0; k <= i-1; k++) { //只在还没构造的节点中找 if(ht[k].parent == -1) { if(ht[k].weight < min1) { min2 = min1; rnode = lnode; min1 = ht[k].weight; lnode = k; } else if(ht[k].weight < min2) { min2 = ht[k].weight; rnode = k; } } } ht[i].weight = ht[lnode].weight + ht[rnode].weight; ht[i].lchild = lnode; ht[i].rchild = rnode; ht[lnode].parent = i; ht[rnode].parent = i; } } //并查集 typedef struct node { int data; //对应此人的编号 int rank; //节点对应的秩 int parent; //节点对应的双亲节点 } UFSTree; void MAKE_SET(UFSTree t[]) { int i; for(i = 1; i<= N;i ++) { t[i].data = i; //数据为此人的编号 t[i].rank = 0; //秩初始化为0 t[i].parent = i; //双亲初始化指向自己 } } //在x所在子树中查找集合编号,递归直到找到祖先 //改进:在找祖先root后,进行路径压缩,把该节点的所有祖先节点的父节点改为root int FIND_SET(UFSTree t[],int x) { if(x!=t[x].parent) return (FIND_SET(t,t[x].parent)); else return (x); } //O(log2 N)树的深度 //合并的时候让较小秩的树根指向较大秩的树根 void UNION(UFSTree t[],int x,int y) { x = FIND_SET(t,x); y = FIND_SET(t,y); if(t[x].rank > t[y].rank) t[y].parent = x; else { t[x].parent = y; if(t[x].rank == t[y].rank) t[t].rank ++; } }