1.1 线性表
线性表的基本运算
1. 查找运算
在线性表中查找具有给定键值的结点
2. 插入运算
在线性表第i(0<=i<=n-1)个结点的前面或者后面插入一个新的结点
3. 删除运算
删除线性表的第i(0<=i<=n-1)个结点。
4. 其他运算
- 统计线性表中节点的个数
- 输出线性表各节点的值
- 复制线性表
- 线性表分拆
- 线性表合并
- 线性表排序
- 按某种规则整理线性表
线性表的存储
-
顺序存储
顺序存储通常用一个数组。
优点:能随机存取线性表中的任何一个结点
缺点:1,数组大小通常是固定的,不利于任意增加或减少线性表节点的个数
2,插入和删除线性表的结点时,要移动数组中的其他元素,操作复杂 -
链式存储
链式存储是用链表存储线性表
优点:每个结点的实际存储位置是任意的,插入和删除操作方便,不需要移动表元
缺点:1,每个节点增加了一个后继指针成分,增加了存储空间
2,不便随机访问线性表的任一结点
在线性表中插入新结点
- 顺序存储
设线性表结点类型为整形,插入之前有n个结点,把值为x的结点插入线性表的第i个位置上
插入步骤为:
-
检查插入要求的有关参数的合理性
-
把原来的第n个结点到第i个结点依次往后移动一个数组元素位置
-
把新结点放在第i个位置上
-
修正线性表的结点个数
-
链式存储
在廉洁存储线性表中插入一个键值为x的新结点,分为如下4种情况
- 在某指针p所指结点之后插入
- 插在首结点之前,使待插入结点成为新的首结点
- 接在线性表的末尾
- 在有序链表中插入,使新的线性表任然有序
删除线性表的结点
- 顺序存储
删除步骤:
-
检查删除要求的有关参数的合理性
-
把原来第i+1个表元至第n个结点依次向前移动一个数组元素位置
-
修正线性表结点个数
-
链式存储
删除步骤:
- 如链表为空链表,则不执行删除操作
- 如链表的首结点的值为指定值,更改表的头指针为指向首结点的后继结点
- 在链表中寻找指定值的结点
- 将找到的结点删除
1.1.1 栈
-
顺序存储
可以用顺序存储线性表来表示栈,为了指明当前执行插入和删除运算的栈顶位置,需要一个地址变量top
指出栈顶结点在数组中的下标 -
链式存储
可以用链表实现。
1.1.2 队列
具有先进先出的特征
- 顺序存储
随着一系列进队和出队运算,会出现前端空着,而队列空间已用完的情况
一种可行的方法是把队列中的结点一刀队列的前端,修改头指针和尾指针
另一种更好的解决方法是采用循环队列
循环队列就是将队列数组的第一个元素与最后一个元素连接起来。
由于其中会造成队空和队满的条件同为head==tail。因此可采用只剩下一个空闲结点,就认为队列已满
- 链式存储
1.1.3 稀疏矩阵
如果一个矩阵的元素绝大部分都为零,则称为稀疏矩阵。若直接用一个二维数组表示稀疏矩阵会浪费大量的
内存空间,通常采用三元组数组或者十字链表两种方法来表示稀疏矩阵
- 三元组数组
稀疏矩阵中的每个非零元素都用一个三元组来表示,即非零元素的 行号,列号,和他的值
然后按照某种顺序将全部非零元素的三元组存入一个数组中
如果只对稀疏矩阵的某些单个元素进行处理,则宜采用三元组表示
相关代码为:
//三元组数值有一个特点:那就是在不同位置上的行值相同的元素
//一定是按照列值升序出现的。
//三元组数值有一个特点:那就是在不同位置上的行值相同的元素
//一定是按照列值升序出现的。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct TRIPLE//三元组成员
{
int row;
int col;
int value;
}TRIPLE;
typedef struct TRIPLE_HEAD//三元组头
{
int rowCount;
int colCount;
int elementCount;
TRIPLE *values;
}TRIPLE_HEAD;
TRIPLE_HEAD *initTriple();
void destoryTriple(TRIPLE_HEAD *triHd);
void showTriple(TRIPLE_HEAD triHd);
TRIPLE_HEAD *revange(TRIPLE_HEAD trip);
TRIPLE_HEAD *revange(TRIPLE_HEAD trip)
{
TRIPLE_HEAD *ntrip = NULL; //new
int p,q,col;
ntrip = (TRIPLE_HEAD *)malloc(sizeof(TRIPLE_HEAD));
ntrip->rowCount = trip.colCount;
ntrip->colCount = trip.rowCount;
ntrip->elementCount = trip.elementCount;
ntrip->values = (TRIPLE *)malloc(sizeof(TRIPLE)*ntrip->elementCount);
if(ntrip->elementCount != 0)
{// 有非零元素则转换
q = 0;
for(col = 1;col <=(trip.colCount);col++)
{
for(p = 0;p < (trip.elementCount);p++)
{
if(trip.values[p].col == col){
ntrip->values[q].row = trip.values[p].col;
ntrip->values[q].col = trip.values[p].row;
ntrip->values[q].value = trip.values[p].value;
q++;
}
}
}
return ntrip;
}
}
void showTriple(TRIPLE_HEAD triHd)
{
int i, j, t = 0;
printf("\n");
for (i = 1; i <= triHd.rowCount; i++)
{
for (j = 1; j <= triHd.colCount; j++)
{
if (t < triHd.elementCount && i == triHd.values[t].row && j == triHd.values[t].col){
printf("%d ", triHd.values[t++].value);
}
else{
printf("0 ");
}
}
printf("\n");
}
}
void destoryTriple(TRIPLE_HEAD *triHd)
{
free(triHd->values);
free(triHd);
}
TRIPLE_HEAD *initTriple()
{
TRIPLE_HEAD *th;
int row;
int col;
int value;
int i;
th = (TRIPLE_HEAD *)malloc(sizeof(TRIPLE_HEAD));
printf("请输入矩阵的阶数(行 列):");
scanf("%d%d", &th->rowCount, &th->colCount);
printf("请输入有效元素的个数:\n");
scanf("%d", &th->elementCount);
th->values = (TRIPLE *)malloc(sizeof(TRIPLE)* th->elementCount);
for (i = 0; i < th->elementCount; i++)
{
printf("请输入第%d个元素(行 列 值)(共%d个):", i+1, th->elementCount);
scanf("%d%d%d", &row, &col, &value);
th->values[i].row = row;
th->values[i].col = col;
th->values[i].value = value;
}
return th;
}
void main(void)
{
TRIPLE_HEAD *trip, *revTrip;
trip = initTriple();
showTriple(*trip);
revTrip = revange(*trip);
showTriple(*revTrip);//转置之后的稀疏矩阵
destoryTriple(trip);
destoryTriple(revTrip);
}
2. 十字链表
在十字链表中,同一行的结点和同一列的结点分别顺序循环连接,每个结点既在他所在行的循环链表中又在他所在列的循环链表中
每个结点含有五个域 行号,列号,值,该结点所在行链表后继结点指针,所在列链表后继结点指针
为了处理方便,通常对每个行链表和列链表分别设置一个头结点,并把它们构成带表头结点的循环链表。
为了引用某行某列方便,全部行链表的表头结点和全部列链表的表头结点分别组成数组,这两个数组的首结点指针存于
一个十字链表的头结点中,最后由一个指针指向该头结点。
相关代码为:
#include <stdio.h>
#include <stdlib.h>
typedef struct Matrix_ELEMENT //稀疏矩阵成员,值 并指向下一个值
{
int row;
int col;
int value;
struct Matrix_ELEMENT *rowLink;
struct Matrix_ELEMENT *colLink;
};
typedef struct Matrix_ELEMENT MELEMENT;
typedef MELEMENT *LINKS; //指向一行或者一列的指针
typedef struct CROSS_LINK
{
int rowCount;
int colCount;
/*LINKS *rowLinks;
LINKS *colLinks;*/ //上下两句等价
MELEMENT **rowLinks;
MELEMENT **colLinks;
}CROSS_LINK;
CROSS_LINK *initCrossLink(void);
MELEMENT *findPreCol(MELEMENT *rowLink, int col);
MELEMENT *findPreRow(MELEMENT *colLink, int row);
void destroyCrossLink(CROSS_LINK *cross);
void showCrossLink(CROSS_LINK *cross);
void showCrossLink(CROSS_LINK *cross)
{
int i, j;
MELEMENT *p;
//输出的技巧
printf("矩阵如下:\n");
for (i = 0; i < cross->rowCount; i++)
{
for (p = cross->rowLinks[i], j = 0; p; p = p->rowLink)
{
while (j++ < p->col)
{
printf("0 ");
}
printf("%d ", p->value);
}
while (j++ < cross->colCount)
{
printf("0 ");
}
printf("\n");
}
}
void destroyCrossLink(CROSS_LINK *cross)
{
//先释放每行指针形成的链表
int i;
MELEMENT *p;
for (i = 0; i < cross->rowCount; i++)
{
while (p = cross->rowLinks[i])
{
p = cross->rowLinks[i];
cross->rowLinks[i] = p->rowLink;
free(p);
}
}
//再释放行链数组和列连数组
free(cross->colLinks);
free(cross->rowLinks);
//最后释放头
free(cross);
}
MELEMENT *findPreRow(MELEMENT *colLink, int row)
{
MELEMENT *p, *q = NULL;
for (p = colLink; p && row >= p->row; p = p->colLink)
{
q = p;
}
return q;
}
MELEMENT *findPreCol(MELEMENT *rowLink, int col)
{
MELEMENT *p, *q = NULL;
for (p = rowLink; p && col >= p->col; p = p->rowLink)
{
q = p;
}
return q;
}
CROSS_LINK *initCrossLink()
{
CROSS_LINK *head;
MELEMENT *mElement, *q;
int row, col;
int value;
printf("请输入稀疏矩阵的阶数(行 列):");
scanf("%d%d", &row, &col);
head = (CROSS_LINK *)malloc(sizeof(CROSS_LINK));
head->rowCount = row;
head->colCount = col;
head->rowLinks = (LINKS *)calloc(sizeof(LINKS),row);
head->colLinks = (MELEMENT **)calloc(sizeof(MELEMENT *), col);
printf("请输入(行 列 值)(行值输入为-1,结束输入):");
scanf("%d%d%d", &row, &col, &value);
while (row != -1) //(row != EOF)
{
//将row、col、he value的值对应到相应的元素
mElement = (MELEMENT *)calloc(sizeof(MELEMENT), 1);
//mElement->rowLink = NULL;
//mElement->colLink = NULL;
mElement->row = row;
mElement->col = col;
mElement->value = value;
if (head->rowLinks[row] == NULL)
head->rowLinks[row] = mElement;
else
{
q = findPreCol(head->rowLinks[row], col);
if (q == NULL)
{
mElement->rowLink = head->rowLinks[row];
head->rowLinks[row] = mElement;
}
else
{
mElement->rowLink = q->rowLink;
q->rowLink = mElement;
}
}
if (head->colLinks[col] == NULL)
head->colLinks[col] = mElement;
else
{
q = findPreRow(head->colLinks[col], row);
if (q == NULL)
{
mElement->colLink = head->colLinks[col];
head->colLinks[col] = mElement;
}
else
{
mElement->colLink = q->colLink;
q->colLink = mElement;
}
}
printf("请输入(行 列 值)(行值输入为-1,结束输入):");
scanf("%d%d%d", &row, &col, &value);
}
return head;
}
void main(void)
{
CROSS_LINK *cLink;
cLink = initCrossLink();
showCrossLink(cLink);
destroyCrossLink(cLink);
system("pause");
}
如果对稀疏矩阵某行或某列整体做某处理,可能会使原来为零的元素变为非零,而原来非零的元素变为零,
对于这种场合,稀疏矩阵应该采用十字链表来表示
KMP算法
相关代码为:
#include <stdio.h>
#include <string.h>
int next[32] = {-999};
/* 返回模式串T在母串S中第pos个字符的位置 */
/* 调试小技巧 print x = value 或 set var x = value 可以改变gdb运行时变量的值 */
int index_BM(char *S, char *T, int pos)
{
int i;
int j;
i = pos;
j = 0;
while ( (i < strlen(S)) && (j < strlen(T)) )
{
if (S[i] == T[j])
{
i++;
j++;
}
else
{
i = i - j + 1;
j = 0;
}
}
/* 注意strlen(T)意味着j的取值范围为0 ~ (strlen(T) - 1) */
if (strlen(T) == j)
{
return i - strlen(T);
}
else
{
return -1;
}
}
void get_next(char *T, int *next)
{
int k = -1;
int j = 0;
next[j] = k;
while (j < strlen(T))
{
if ( (k == -1) || (T[j] == T[k]) ) //注意等号是==,而不是=
{
++k; // 注意是先加后使用
++j;
next[j] = k;
}
else
{
k = next[k];
}
}
}
int index_KMP(char *S, char *T, int pos)
{
int i;
int j;
i = pos;
j = 0;
while ( (i < strlen(S)) && (j < strlen(T)) )
{
/* j = -1 表示next[0], 说明失配处在模式串T的第0个字符。所以这里特殊处理,然后令i+1和j+1。*/
if ( (j == -1) || S[i] == T[j])
{
i++;
j++;
}
else
{
j = next[j];
}
}
if (strlen(T) == j)
{
return i - strlen(T);
}
else
{
return -1;
}
}
void print_next(int next[], int n)
{
int i;
for (i = 0; i < n; i++)
{
printf("next[%d] = %d\n", i, next[i]);
}
}
int main(void)
{
char *s = "ababcabcacbab";
char *t = "abcac";
int pos = 0;
int index;
printf("================ BM ==============\n");
index = index_BM(s, t, pos);
printf("index = %d\n", index);
printf("================ KMP ==============\n");
get_next(t, next);
print_next(next, strlen(t));
index = index_KMP(s, t, pos);
printf("index = %d\n", index);
}
求next函数的值
next[j] = 0 j = 1;
next[j] = max{k|1 < k < j,'p1p2...pk-1' = 'pj-k+1pj-k+2...pj-1'}
next[j] = 1 其他情况