1、线性表的特征
线性表中每个数据元素有且仅有一个直接前驱和一个直接后继,第一个结点无前驱,最后一个结点无后继。
2、线性表的存储方式
-
线性表顺序存储(顺序表):静态分配,借助高级语言的数组,连续的地址空间存放表元素,逻辑关系隐含在存储顺序中。
-
线性表链式存储(链表):动态分配,借助高级语言的指针,动态申请与动态释放地址空间,结点的物理存储可以是不连续的,逻辑关系通过指针表明。
3、顺序表与链表
4、顺序表与链表的比较
(1)、基于空间的考虑
(2)、基于时间的考虑
5、各种链表的比较
6、线性表案例
(1)一元多项式的表示的相加
Java代码(参考别人的,知道算法思想就好,没必要自己再写一遍)
// 节点类
class PolynomialNode {
private int coef;// 存储的一元多项式的系数
private int expon;// 存储的一元多项式的指数
private PolynomialNode next;// 下一个结点引用指针
public PolynomialNode() {
super();
}
public PolynomialNode(int coef, int expon, PolynomialNode next) {
super();
this.coef = coef;
this.expon = expon;
this.next = next;
}
public int getCoef() {
return coef;
}
public void setCoef(int coef) {
this.coef = coef;
}
public int getExpon() {
return expon;
}
public void setExpon(int expon) {
this.expon = expon;
}
public PolynomialNode getNext() {
return next;
}
public void setNext(PolynomialNode next) {
this.next = next;
}
}
public class OneVariablePolynomialDemo {
/**
* 判断两个指数谁大
*
* @param e1 指数1
* @param e2 指数2
* @return 如果e1>e2返回1,如果e1<e2返回-1,如果e1=e2返回0
*/
private static int compare(int e1, int e2) {
// 比较两项指数e1和e2,根据大小等三种情况返回1、-1,0
if (e1 > e2) {
return 1;
} else if (e1 < e2) {
return -1;
} else {
return 0;
}
}
/**
* 添加新结点到结果链表
*
* @param coef 系数
* @param expon 指数
* @param rearNode 结果链表
* @return 返回添加成功的结果链表
*/
public static PolynomialNode attach(int coef, int expon, PolynomialNode rearNode) {
// 创建一个新结点并存储指数项和系数项
PolynomialNode newNode = new PolynomialNode();
newNode.setCoef(coef);
newNode.setExpon(expon);
newNode.setNext(null);
// 将新结点插入到当前结果表达式的后面,现在尾结点变成了newNode
rearNode.setNext(newNode);
// 将尾结点指向新插入的结点newNode
rearNode = newNode;
// 返回链表
return rearNode;
}
/**
* * 输入的各种情况:
* * (1)如果系数为1,则不输出系数;(需要判断指数是否为1或者为0亦或者为负数的情况)
* * (2)如果系数为0,则不输出任何;
* * (3)如果系数小于0,则在括号内输出负数;(需要判断指数是否为1或者为0亦或者为负数的情况)
* * (4)如果指数为1,则不输出指数;
* * (5)如果指数为0并且系数不为0则输出系数;
* * (6)如果指数是负数则在括号内输出负数;
*
* @param node 要打印的结点
*/
public static void printNode(PolynomialNode node) {
String polynomial = "";
// 循环链表
while (node != null) {
// 判断系数和指数
if (node.getCoef() == 1) {
if (node.getExpon() == 1) {
polynomial += "+x";
} else if (node.getExpon() == 0) {
polynomial += "+" + node.getCoef();
} else if (node.getExpon() < 0) {
polynomial += "+" + node.getCoef() + "x^(" + node.getExpon() + ")";
} else {
polynomial += "+" + "x^" + node.getExpon();
}
} else if (node.getCoef() == 0) {
polynomial += "";
} else if (node.getCoef() < 0) {
if (node.getExpon() == 1) {
polynomial += "+(" + node.getCoef() + ")x";
} else if (node.getExpon() == 0) {
polynomial += "+(" + node.getCoef() + ")";
} else if (node.getExpon() < 0) {
polynomial += "+(" + node.getCoef() + ")x^(" + node.getExpon() + ")";
} else {
polynomial += "+(" + node.getCoef() + ")x^" + node.getExpon();
}
} else {
if (node.getExpon() == 1) {
polynomial += "+" + node.getCoef() + "x";
} else if (node.getExpon() == 0) {
polynomial += "+" + node.getCoef();
} else if (node.getExpon() < 0) {
polynomial += "+" + node.getCoef() + "x^(" + node.getExpon() + ")";
} else {
polynomial += "+" + node.getCoef() + "x^" + node.getExpon();
}
}
node = node.getNext();
}
System.out.println(polynomial.substring(1, polynomial.length()));
}
/**
* 一元多项式相加
*
* @param p1 一元多项式链表1
* @param p2 一元多项式链表2
* @return 返回相加的结果
*/
public static PolynomialNode polyAdd(PolynomialNode p1, PolynomialNode p2) {
PolynomialNode front, rear, temp;
int sum;
// 为方便表头插入,先产生一个临时空结点作为结果多项式链表头
rear = new PolynomialNode();
// 由front记录结果多项式头结点
front = rear;
// 当两个多项式都有非零项待处理时
while (p1 != null && p2 != null) {
// 根据比较结点的指数返回的值进行操作
switch (compare(p1.getExpon(), p2.getExpon())) {
// P1中的数据项指数较大时
case 1:
// 将P1的结点放到结果链表中并返回成功的结果链表
rear = attach(p1.getCoef(), p1.getExpon(), rear);
// 并将P1结点指向它的下一个结点
p1 = p1.getNext();
break;
// P2中的数据项指数较大时
case -1:
// 将P2的结点放到结果链表中并返回成功的结果链表
rear = attach(p2.getCoef(), p2.getExpon(), rear);
// 将P2结点指向它的下一个结点
p2 = p2.getNext();
break;
// 两数据项指数相等
case 0:
// 当指数相等时,让系数相加
sum = p1.getCoef() + p2.getCoef();
// 如果相加的系数不为0(大于或小于0都可)
if (sum != 0) {
//将该结点放到结果链表中并返回成功的结果链表
rear = attach(sum, p1.getExpon(), rear);
}
// 让P1结点和P2结点都指向下一个结点元素
p1 = p1.getNext();
p2 = p2.getNext();
break;
default:
break;
}
}
// 将未处理完的另一个多项式的所有结点依次复制到结果链表中
for (; p1 != null; p1 = p1.getNext()) {
rear = attach(p1.getCoef(), p1.getExpon(), rear);
}
for (; p2 != null; p2 = p2.getNext()) {
rear = attach(p2.getCoef(), p2.getExpon(), rear);
}
rear.setNext(null);
temp = front;
// 使front指向结果多项式的第一个非零项
front = front.getNext();
return front;
}
public static void main(String[] args) {
// 注意:一元多项式的各项要按照指数递减的顺序排列各项
Scanner scanner = new Scanner(System.in);
// 获取第一个多项式链表
System.out.println("【注意:一元多项式的各项要按照指数递减的顺序排列各项】");
System.out.print("请输入第一个多项式的项数:");
PolynomialNode p1 = new PolynomialNode();
// 保存头结点
PolynomialNode front1 = p1;
// 获取第一个多项式的项数
int itemCount = scanner.nextInt();
// 循环获取多项式的系数和指数
for (int i = 1; i <= itemCount; i++) {
System.out.print("第" + i + "项的系数是:");
int coef = scanner.nextInt();
System.out.print("第" + i + "项的指数是:");
int expon = scanner.nextInt();
// 创建新结点,将数据赋给结点元素
PolynomialNode p = new PolynomialNode(coef, expon, null);
p1.setNext(p);
p1 = p;
}
// 打印第一个多项式
System.out.print("p1(x)=");
printNode(front1);
// 获取第二个多项式链表
System.out.print("请输入第二个多项式的项数:");
PolynomialNode p2 = new PolynomialNode();
// 保存头结点
PolynomialNode front2 = p2;
// 获取第二个多项式的项数
int itemCount2 = scanner.nextInt();
// 循环获取多项式的系数和指数
for (int i = 1; i <= itemCount2; i++) {
System.out.print("第" + i + "项的系数是:");
int coef = scanner.nextInt();
System.out.print("第" + i + "项的指数是:");
int expon = scanner.nextInt();
PolynomialNode p = new PolynomialNode(coef, expon, null);
p2.setNext(p);
p2 = p;
}
// 打印第一个多项式
System.out.print("p2(x)=");
printNode(front2);
// 计算两个一元多项式相加
System.out.print("p1(x)+p2(x)=");
printNode(polyAdd(front1, front2));
}
}
测试
代码还有可以改进的地方,比如可以不需要提前输入项数,和多项式的各项不需要要按照指数递减的顺序排列各项
7、纸质练习题
单选题
1、某线性表最常用的操作是取第i个元素和找第i个元素的前驱元素,采用()存储方式最省时间
A、单链表
B、顺序表
C、双链表
D、单循环链表
2、双向链表存储结构中,删除s所指的节点时需修改指针()
A、s->next=s->next->next;s->next->prior=s;
B、s->prior->next=s;s->prior=s->prior->prior;
C、s->prior=s->next->next;s->next=s->prior->prior;
D、s->prior->next=s->next;s->next->prior=s->prior;
3、在双向循环链表中,在p指针所指的节点后插入q所指向的新节点,其修改指针的操作是()
A、p->next=q;q->prior=p;p->next->prior=q;q->next=q;
B、p->next->prior=q;q->next=p->next;q->prior=p;p->next=q;
C、p->next=q;p->next->prior=q;q->prior=p;q->next=p->next;
D、q->prior=p;q->next=p->next;p->next=q;p->next->prior=q;
4、执行下面程序段时,语句s的执行次数为()
for(i=0;i<=n;i++)
for(j=0;j<=i;j++){
s;
}
A、n的平方
B、n的平方 / 2
C、n(n+1)
D、(n+1)(n+2)/2
6、顺序表的存储密度()
A、大于1
B、等于1
C、小于1
D、不能确定
多选题
1、算法的设计要求是()
A、可行性
B、有限性
C、正确性
D、可读性
E、健壮性
F、高效率和低存储量
填空题
1、带头结点的单链表L中,利用头插法插入节点s,语序序列是( )( )
提示: 用下面的表达式
s->next
L->next
s
L
判断题
1、双向链表中,节点前插入节点,最重要的是p->prior这条关系线,应该放最后修改。
2、线性结构只能用顺序结构来存放,非线性结构只能用非顺序结构来存放。
3、顺序存储方式的优点是存储密度大,且插入、删除操作效率高。
4、链式存储方式的优点是存储密度大,接近于1,且插入、删除操作效率高。
简答题
1、在双向循环链表中,在p指针所指的节点前插入q所指向的新节点,其修改指针的操作(可以画图)
2、头指针、头结点、首元结点的区别是?
3、单链表L中,若要删除由指针q所指向节点的后继节点,借助空指针s,语句序列是什么?(看画图)
答案:
1、选择题 BDBDDB
第四题
2、多选题 CDEF
3、填空题
第一空: s->next=L->next;
第二空: L->next=s;
3、判断题
√ × × ×
4、简答题
(1)
(2)头指针、头结点、首元结点的区别是?
正确答案:
头指针是指向头结点的指针
头节点是指链表中的第一个节点,头结点不一定是首元节点
首元节点是链表中的第一个元素节点
(3)
s=q->next;
q->next=s->next;
delete s;