引言
我们前面学习了线性表的顺序存储,顺序表的链式存储,但是在实际运用过程中我们该如何应用呢?
应用举例
应用一:多项式的处理
多项式表示
与 相加
:
- 设一元n次多项式:
它的n+1个系数可形成一个线性表:p(p0,p1,… ,pn)
,而x的指数i(0 ≤i ≤ n)
对应系数 pi 的序号。无疑Pn(x)中有许多系数为0的项,如:
我们看个多项式的案例:
对于这样繁琐的多项式合并计算,如果多了,我们应该如何解决快速计算,可以用线性表来解决。我们要抽取处理实际应用中线性表是哪一个?
一个线性多项式,用一个线性表来表示。(只考虑有效项)
对应的线性表就是P(1,0,……,0,3,……,2)
. 显然这些0元素在存储结构中要占用大量的存储空间,是一种浪费,且处理起来费时,所以去掉Pn(x)中系数为0的项,写作:
其中:Pi(1 ≤ i ≤ m) ≠ 0
,且 0≤ el < e2 < e3 <…< em = n
,于是,系数的序号 i 并不反映x的指数ei,故对应的线性表每项要用两个值表示,即:
如P2000(x)
,
对应的线性表为:
P( (1,0) , (3,1000) , (2,2000) )
。
讨论用单链表的形式存储多项式及两个多项式相加。
将多形式中的一项存储于一个结点,结点类型描述如图
结点类型描述
typedef struct
{
float coef;
int exp;
}data_t;
typedef struct node_t
{
data_t data;
struct node_t *next;
}linknode_t , *linklist_t;
例如 设两多项式A,B分别为:
- C为A,B多项式的合并
我们思考,如何形成多项式A,B的链表结构,可以参照我们之前“建立链表的算法”。
A和B的链表结构如图:
多项式的合并,就是两个链表,需要两个指针,分别指向两个链表中的第一个结点,根据结点的指数域(exp)
去比较,如果遇到,指数相同就相加,遇到指数小的,先去合并。
- 算法思路:
- 设指针pa,pb分别指向两链表中的某个结点(初始指向第一个结点);
- 若
pa->data.exp < pb->data.exp
,则pa结点应为和的一项; - 若
pa->data.exp > pb->data.exp
,则pb结点应为和的一项; - 若
pa->data.exp = pb->data.exp
,则两结点对应系数相加;
{sum = pa->data.coef + pb->data.coef
} - sum != 0 , 相加结果应为和的一项。
结果链表图为
应用二:Joseph问题(约瑟夫算法)
Joseph问题:
设编号分别为:1,2,…,n的n个人围坐一圈。约定序号为 k (1 ≤ k ≤ n)
的人, 从1开始计数,数到m的那个人出列,他的下一位又从1开始计数,数到m的那个人又出列,依次类推,直到所有人出列为止。
例2-9设n=8,k=3,m=4时
,如图所示。
比如有8个人坐一圈,3号队员开始从1数数,依次类推,当6号队员应该数4(m)的时候,他出列,类推下去,直到所有人出列。
出列序列为:(6,2,7,4,3,5,1,8)
。
算法思路:
用一个不带头结点的循环链表来处理Josephu问题:
- 先构想结构体的定义
- 建立一个有n个结点的(无头结点)单循环链表
- 验证链表的正确性:遍历链表,
- 然后从第k结点起从1计数,计到m时,对应结点从链表中删除;
- 然后再从被删除结点的下一个结点起又从1开始计数……,直到所有结点都出列时算法结束。
算法对应的链表结构如图所示。
joseph代码思路构建:
- 单项循环链表的建立
第一步:先构想结构体的定义
#ifndef _JOSEPH_H__
#define _JOSEPH_H__
#include<stdlib.h>
#include<stdio.h>
typedef struct node{
int data;
struct node *next;
}listnode, *linklist;
extern linklist list_create();
extern void list_show(linklist H);
#endif
第二步:建立一个有n个结点的(无头结点)单循环链表,并且验证链表的正确性:遍历链表,
#include"joseph.h"
linklist list_create(){
linklist H,r,p;
int n;
int i;
loop: