目录
在长度为n的顺序表sq的第i个位置上插入一个新的元素item
1.1线性表的基本概念
1.1.1线性表的定义
a=(a1,a2,a3,……,ai-1,ai,ai+1,……,an)
当1<i<n时,ai的直接前驱为ai-1,ai的直接后驱为ai+1;除了第一个元素和最后一个元素,序列中任何一个元素有且仅有一个直接前驱元素和一个直接后继元素;数据元素之间的先后顺序为“一对一”的关系。
数据元素之间具有的逻辑关系为线性关系的数据元素集合称为线性表,n为线性表的长度,长度为零的线性表称为为空表。
1.1.2线性表的基本操作
1.创建一个新的线性表。
2.求线性表的长度。
3.检索线性表中第i个数据元素。(1≤i≤n)
4.根据数据元素的某数据项(通常称为关键字)的值求该数据元素在线性表中的位置。
5.在线性表的第i个位置上存入一个新的数据元素。6.在线性表的第i个位置上插入一个新的数据元素。.7.删除线性表中第i个数据元素。
8.对线性表中的数据后素按照某一个数据项的值的大小做升序或者降序排序。
1.2线性表的顺序储存结构
1.2.1构造原理
用一组地址连续的存储单元依次存储线性表的数据元素,数据元素之间的逻辑关系通过数据元素的存储位置直接反映。所谓一个元素的地址是指该元素占用的k个(连续的)存储单元的第一个单元的地址。

若假设每个数据元素占用k个存储单元,并且已知第一个元素的存储位置LOC(a1),则有
LOC(ai)= LOC(a1)+(i-1)*k

#define MaxSize 120
typedef struct{
ElemType a[MaxSize]; //顺序表的元素
int n; //顺序表的长度
}SeqList;
1.2.2基本算法
确定元素item在长度为n的顺序表sq中的位置
int findPos(SeqList sq,ElemType item){
for(int i=0;i<sq.n;i++){
if(sq.a[i]==item) return i+1; //找到元素等于item时返回元素位置
}
return -1; //未找到返回-1
}
在长度为n的顺序表sq的第i个位置上插入一个新的元素item
int insert(SeqList *sq,int i,ElemType item){
if(sq->n==MaxSize||i>(sq->n+1)||i<1) return -1; //表满或插入位置不对
for(int j=sq->n;j>=i;j++){
sq->a[j]=sq->a[j-1]; //依次后移一个位置
}
sq->a[i-1]=item; //将item插入表的第i个位置
sq->n++; //表长加1
return 1; //插入成功
}
特点
优点:(1) 构造原理简单、直观,易理解。
(2) 元素的存储地址可以通过一个简单的解析式计算出来。是一种随机存储结构,存储速度 快。
(3) 由于只需存放数据元素本身的信息,而无其他空间开销,相对链式存储结构而言,存储 空间开销小
缺点:(1) 存储分配需要事先进行。
(2) 需要一片地址连续的存储空间。
(3) 基本操作(如插入、删除)的时间效率较低。
例题
1.顺序表的优点是( C )
A. 插入操作的时间效率高
B. 适用于各种逻辑结构的存储表示
C. 存储密度(存储利用率)高
D. 删除操作的时间效率高
2.在长度为n的顺序表的第i个位置插入一个元素,则i的合法值应该是( B )
A. 1≤i≤n
B. 1≤i≤n+1
C. 0≤i≤n-1
D. 0si≤n
3.若长度为n的线性表采用顺序结构,在第i个数据元素之前插入一个元素,需要它依次向后移动 ( C )个元素。
A. n-i
B. i
C. n-i+1
D. n-i-1
4.长度为n的顺序表,算法的时间复杂度为O(1)的操作是( ABC )
A. 访问第i(1≤i≤n)个元素
B. 求第i个元素(2≤i≤n)的直接前驱
C. 删除第n个元素
D. 在第i(1≤i≤n)个元素前插入一个新元素
1.3线性表的链式储存结构
1.3.1线性链表的构造原理
用一组地址任意的存储单元(连续的或不连续的)依次存储表中各个数据元素,数据元素之间的逻辑关系通过指针间接地反映出来。

typedef struct node{
ElemType data;
struct node *next;
}LNode,*LinkList;
1.3.1基本算法
求链表长度
非递归算法
int Length(LinkList head){
LinkList p=head;
int n=0;
while(p){
p=p->next;
n++;
}
return n;
}
递归算法
int Length(LinkList head){
if(head!=NULL) return 1+Length(head->next);
else return 0;
}
特点
优点
(1)存储空间动态分配,可以根据实际需要使用
(2)不需要地址连续的存储空间。
(3)插入/删除操作只须通过修改指针实现,不必移动数据元素,操作的时间效率较高。
缺点
(1)每个链结点需要设置指针域(存储密度小)。
(2)是一种非随机存储结构,查找、定位等操作要
通过顺序扫描链表实现,时间效率较低。
例题
1.链表不具有的特点是( B )
A. 插入、删除不需要移动元素
B. 方便随机访问任一元素
C. 不必事先估计存储空间
D. 所需空间与线性长度成正比
2.线性表若采用链式存储结构时,要求内存中可用存储单元的地址( B )
A. 必须是连续的
B. 连续或不连续都可以
C. 部分地址必须是连续的
D. 一定是不连续的
3.在单链表中,要删除某一指定结点,必须先找到该结点的( A )
A. 直接前驱
B. 自身位置
C. 直接后继
D. 直接后继的后继
1.4例题
判断题
1.在单链表中,要访问某个结点,只要知道该结点的指针即可。因此,单链表是一种随机存取结构。 (×)
2.线性表的逻辑顺序与物理顺序总是一致的。 (×)
3.线性表的插入、删除总是伴随着大量数据的移动。 (×)
4.若某线性表最常用的操作是存取任一指定序号的元素和在最后进行插入和删除运算,则利用顺序表存储最节省时间。 (√)
5.若用链表来表示一个线性表,则表中元素的地址一定是连续的。 (×)
单选题
1.在数据结构中,从逻辑上可以把数据结构分成( C )。
A. 动态结构和静态结构
B. 紧凑结构和非紧凑结构
C. 线性结构和非线性结构
D. 内部结构和外部结构
2.与数据元素本身的形式、内容、相对位置、个数无关的是数据的( C )。
A. 存储结构
B. 存储实现
C. 逻辑结构
D. 运算实现
3.以下数据结构中,( A )是非线性数据结构。
A. 树
B. 字符串
C. 队列
D. 栈
4.若某线性表最常用的操作是存取任一指定序号的元素和在最后进行插入和删除运算,则利用哪种存储方式最节省时间?( D )
A. 双链表
B. 单循环链表
C. 带头结点的双循环链表
D. 顺序表
5.顺序表中第一个元素的存储地址是100,每个元素的长度为2,则第5个元素的地址是( C )。
A. 100
B. 105
C. 108
D. 110
5.以下说法正确的是( D )。
A. 数据元素是数据的最小单位
B. 数据项是数据的基本单位
C. 数据结构是带有结构的各数据项的集合
D. 一些表面上很不相同的数据可以有相同的逻辑结构
6.已知一个长度为16的顺序表L,其元素按关键字有序排列。若采用二分查找法查找一个L中不存在的元素,则关键字的比较次数最多是:( B )
A. 4
B. 5
C. 6
D. 7
7.链表不具有的特点是:( A )
A. 插入、删除不需要移动元素
B. 方便随机访问任一元素
C. 不必事先估计存储空间
D. 所需空间与线性长度成正比
8.带头结点的单链表h为空的判定条件是:( B )
A. h == NULL;
B. h->next == NULL;
C. h->next == h;
D. h != NULL;
9. 在具有N个结点的单链表中,实现下列哪个操作,其算法的时间复杂度是O(N)?( C )
A. 在地址为p的结点之后插入一个结点
B. 删除开始结点
C. 遍历链表和求链表的第i个结点
D. 删除地址为p的结点的后继结点
10.线性表L在什么情况下适用于使用链式结构实现?( A )
A. 需不断对L进行删除插入
B. 需经常修改L中的结点值
C. L中含有大量的结点
D. L中结点结构复杂
11. The following table shows how a linked list is stored in memory space with the head node c:

Now f is stored at 1014H and is inserted into the linked list between a and e. Then the "Link"
fields of a, e, and f are __, respectively.( D )
A. 1010H, 1014H, 1004H
B. 1010H, 1004H, 1014H
C. 1014H, 1010H, 1004H
D. 1014H, 1004H, 1010H
12.在单链表中,要删除某一指定结点,必须先找到该结点的( A )。
A. 直接前驱
B. 自身位置
C. 直接后继
D. 直接后继的后继
13.以下关于链式存储结构的叙述中,( C )是不正确的。
A.结点除自身信息外还包括指针域,因此存储密度小于顺序存储结构
B.逻辑上相邻的结点物理上不必邻接
C.可以通过计算直接确定第i个结点的存储地址
D.插入、删除运算操作方便,不必移动结点
14.已知头指针 h 指向一个带头结点的非空单循环链表,结点结构为 data | next,其中 next 是指向直接后继结点的指针,p 是尾指针,q 是临时指针。现要删除该链表的第一个元素,正确的语句序列是:( D )
A.h->next=h->next->next; q=h->next; free(q);
B.q=h->next; h->next=h->next->next; free(q);
C.q=h->next; h->next=q->next; if (p!=q) p=h; free(q);
D.q=h->next; h->next=q->next; if (p==q) p=h; free(q);
函数题
1.顺序表的删除操作
本题要求实现一个函数,要求删除顺序表第i个位置的元素,成功返回1,失败返回-1;
函数接口定义:
//删除顺序表第i个位置的元素
int del(SeqList *sq,int i);
其中SeqList结构定义如下:
typedef struct{
ElemType a[MaxSize];//顺序表的元素
int n; //顺序表的长度
}SeqList;
裁判测试程序样例:
#include <stdio.h>
#define MaxSize (100) //纯C语法,定义一个符号常量
typedef int ElemType; //当前顺序表的数据类型是整型
typedef struct{
ElemType a[MaxSize];//顺序表的元素
int n; //顺序表的长度
}SeqList;
//输出顺序表sq,细节不表
void print(SeqList sq);
//创建顺序表sq,细节不表
void create(SeqList *sq);
//删除顺序表第i个位置的元素
int del(SeqList *sq,int i);
int main()
{
SeqList sq;
create(&sq);
int i;
scanf("%d",&i);
int res = del(&sq,i);
if(res == -1){
printf("Delete Error.The value of i is illegal!");
}else if(res == 1){
printf("Delete Success.The elements of the SequenceList are:\n");
print(sq);
}
return 0;
}
// 请在这里填写答案
输入样例:
2 6 4 -1 1
输出样例:
Delete Success.The elements of the SequenceList are:
6 4
代码
//删除顺序表第i个位置的元素
int del(SeqList *sq,int i){
if(i>MaxSize||i>sq->n||i<1) return -1;
for(int j=i-1;j<sq->n-1;j++){
sq->a[j]=sq->a[j+1];
}
sq->n++;
return 1;
}
2.求链式表的表长
本题要求实现一个函数,求链式表的表长。
函数接口定义:
int Length( List L );
其中List结构定义如下:
typedef struct LNode *PtrToLNode;
struct LNode {
ElementType Data;
PtrToLNode Next;
};
typedef PtrToLNode List;
L是给定单链表,函数Length要返回链式表的长度。
裁判测试程序样例:
#include <stdio.h>
#include <stdlib.h>
typedef int ElementType;
typedef struct LNode *PtrToLNode;
struct LNode {
ElementType Data;
PtrToLNode Next;
};
typedef PtrToLNode List;
List Read(); /* 细节在此不表 */
int Length( List L );
int main()
{
List L = Read();
printf("%d\n", Length(L));
return 0;
}
/* 你的代码将被嵌在这里 */
输入样例:
1 3 4 5 2 -1
输出样例:
5
代码
int Length( List L ){
int len=0;
while(L){
L=L->Next;
len++;
}
return len;
}
3.递增的整数序列链表的插入
本题要求实现一个函数,在递增的整数序列链表(带头结点)中插入一个新整数,并保持该序列的有序性。
函数接口定义:
List Insert( List L, ElementType X );
其中List结构定义如下:
typedef struct Node *PtrToNode;
struct Node {
ElementType Data; /* 存储结点数据 */
PtrToNode Next; /* 指向下一个结点的指针 */
};
typedef PtrToNode List; /* 定义单链表类型 */
L是给定的带头结点的单链表,其结点存储的数据是递增有序的;函数Insert要将X插入L,并保持该序列的有序性,返回插入后的链表头指针。
裁判测试程序样例:
#include <stdio.h>
#include <stdlib.h>
typedef int ElementType;
typedef struct Node *PtrToNode;
struct Node {
ElementType Data;
PtrToNode Next;
};
typedef PtrToNode List;
List Read(); /* 细节在此不表 */
void Print( List L ); /* 细节在此不表 */
List Insert( List L, ElementType X );
int main()
{
List L;
ElementType X;
L = Read();
scanf("%d", &X);
L = Insert(L, X);
Print(L);
return 0;
}
/* 你的代码将被嵌在这里 */
输入样例:
5
1 2 4 5 6
3
输出样例:
1 2 3 4 5 6
代码
List Insert(List L,ElementType X){
List p =(List)malloc(sizeof(struct Node)); //初始化一个指针p
p->Data = X; //将X赋给p
List n = L; //复制L给n,后续对n进行操作,L一样会变化,但最后返回L是头节点
while(n->Next&&n->Next->Data<X){
n=n->Next; //当n下一节点不为空且值小于X,指向下一节点
}
p->Next= n->Next;
n->Next=p;
return L; //返回插入后的链表
}
4.查找中间结点
函数find_middle()实现了对给定的单链表,查找到其中间结点。如果中间结点为两个,返回前面的那个结点的地址。请完成该函数。
函数接口定义:
Node* find_middle(Node* head);
head是单链表的头指针,函数返回查找到的结点地址。
裁判测试程序样例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct ListNode {
int num;
struct ListNode *next;
}Node;
Node *createlist(); /*裁判实现,细节不表*/
Node* find_middle(Node* head);
void display(Node *head);/*裁判实现,细节不表*/
int main()
{
Node *head,*p;
head = createlist();
p = find_middle(head);
display(p);
return 0;
}
/* 请在这里填写答案 */
输入样例1:
5
1 3 7 9 10
输出样例1:
7
输入样例2:
6
1 2 3 5 6 7
输出样例2:
3
代码
Node* find_middle(Node* head){
Node* p=head; //复制头节点给P
int len=0; //初始化计数器
while(p){
p=p->next;
len++; //计算链表长度
}
len/=2; //中间位置
while(len){
head=head->next;
len--; //计数器归零
}
return head;
}
4.求链表的倒数第m个元素
请设计时间和空间上都尽可能高效的算法,在不改变链表的前提下,求链式存储的线性表的倒数第m(>0)个元素。
函数接口定义:
ElementType Find( List L, int m );
其中List结构定义如下:
typedef struct Node *PtrToNode;
struct Node {
ElementType Data; /* 存储结点数据 */
PtrToNode Next; /* 指向下一个结点的指针 */
};
typedef PtrToNode List; /* 定义单链表类型 */
L是给定的带头结点的单链表;函数Find要将L的倒数第m个元素返回,并不改变原链表。如果这样的元素不存在,则返回一个错误标志ERROR。
裁判测试程序样例:
#include <stdio.h>
#include <stdlib.h>
#define ERROR -1
typedef int ElementType;
typedef struct Node *PtrToNode;
struct Node {
ElementType Data;
PtrToNode Next;
};
typedef PtrToNode List;
List Read(); /* 细节在此不表 */
void Print( List L ); /* 细节在此不表 */
ElementType Find( List L, int m );
int main()
{
List L;
int m;
L = Read();
scanf("%d", &m);
printf("%d\n", Find(L,m));
Print(L);
return 0;
}
/* 你的代码将被嵌在这里 */
输入样例:
5
1 2 4 5 6
3
输出样例:
4
1 2 4 5 6
代码
ElementType Find( List L, int m ){
List p=L,q=L;
int len=0; //初始化计数器
while(p->Next){
p=p->Next;
len++;
if(len>=m) q=q->Next; //正数第n-m个元素就是倒数第m个
}
return q==L?ERROR:q->Data; //找不到元素则返回ERROR,否则返回元素值
}
5.链表逆置
本题要求实现一个函数,将给定单向链表逆置,即表头置为表尾,表尾置为表头。
函数接口定义:
struct ListNode *reverse( struct ListNode *head );
其中head是用户传入的链表的头指针;函数reverse将链表head逆置,并返回结果链表的头指针。
裁判测试程序样例:
#include <stdio.h>
#include <stdlib.h>
struct ListNode {
int data;
struct ListNode *next;
};
struct ListNode *createlist(); /*裁判实现,细节不表*/
struct ListNode *reverse( struct ListNode *head );
void printlist( struct ListNode *head )
{
struct ListNode *p = head;
while (p) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
int main()
{
struct ListNode *head;
head = createlist();
head = reverse(head);
printlist(head);
return 0;
}
/* 你的代码将被嵌在这里 */
输入样例:
1 2 3 4 5 6 -1
输出样例:
6 5 4 3 2 1
代码
struct ListNode *reverse( struct ListNode *head ){
struct ListNode *p=NULL,*t;
while(head->next){
t=head->next; //保留下一节点给t暂时储存
head->next=p; //头节点断开,再连接p
p=head; //p保存新的头节点
head=t; //从t拿回
}
return p; //逆置完成
}
6.两个有序链表序列的合并
本题要求实现一个函数,将两个链表表示的递增整数序列合并为一个非递减的整数序列。
函数接口定义:
List Merge( List L1, List L2 );
其中List结构定义如下:
typedef struct Node *PtrToNode;
struct Node {
ElementType Data; /* 存储结点数据 */
PtrToNode Next; /* 指向下一个结点的指针 */
};
typedef PtrToNode List; /* 定义单链表类型 */
L1和L2是给定的带头结点的单链表,其结点存储的数据是递增有序的;函数Merge要将L1和L2合并为一个非递减的整数序列。应直接使用原序列中的结点,返回归并后的带头结点的链表头指针。
裁判测试程序样例:
#include <stdio.h>
#include <stdlib.h>
typedef int ElementType;
typedef struct Node *PtrToNode;
struct Node {
ElementType Data;
PtrToNode Next;
};
typedef PtrToNode List;
List Read(); /* 细节在此不表 */
void Print( List L ); /* 细节在此不表;空链表将输出NULL */
List Merge( List L1, List L2 );
int main()
{
List L1, L2, L;
L1 = Read();
L2 = Read();
L = Merge(L1, L2);
Print(L);
Print(L1);
Print(L2);
return 0;
}
/* 你的代码将被嵌在这里 */
输入样例:
3
1 3 5
5
2 4 6 8 10
输出样例:
1 2 3 4 5 6 8 10
NULL
NULL
代码
List Merge(List L1,List L2){
List a,c,b,l;
l=(List)malloc(sizeof(PtrToNode));
a=L1->Next,b=L2->Next,c=l; //为了保留L1 L2 l的指针指向头节点
while(a&&b){ //当L1L2不为空
if(a->Data>b->Data){
c->Next=b;
b=b->Next;
}
else{
c->Next=a;
a=a->Next;
}
c=c->Next;
}
if(a) c->Next=a;
if(b) c->Next=b;
L1->Next=NULL;
L2->Next=NULL; //根据输出结果将L1L2清空
return l;
}
编程题
1.数组循环左移
本题要求实现一个对数组进行循环左移的简单函数:一个数组a中存有n(>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向左移m(≥0)个位置,即将a中的数据由(a0a1⋯an−1)变换为(am⋯an−1a0a1⋯am−1)(最前面的m个数循环移至最后面的m个位置)。如果还需要考虑程序移动数据的次数尽量少,要如何设计移动的方法?
输入格式:
输入第1行给出正整数n(≤100)和整数m(≥0);第2行给出n个整数,其间以空格分隔。
输出格式:
在一行中输出循环左移m位以后的整数序列,之间用空格分隔,序列结尾不能有多余空格。
输入样例:
8 3
1 2 3 4 5 6 7 8
输出样例:
4 5 6 7 8 1 2 3
代码
#include<bits.stdc++.h>
using namespace std;
int main()
{
int n,m,a[120];
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
m=m%n; //取余,简化操作
int num=0;
for(int i=m+1;i<=2*n;i++){
num++;
int j=i%n==0?n:i%n; //注意特判a[n]位置的输出
if(num==n){
cout<<a[j]<<"\n";
break;
}
else cout<<a[j]<<" ";
}
}
2.字符串的冒泡排序
我们已经知道了将N个整数按从小到大排序的冒泡排序法。本题要求将此方法用于字符串序列,并对任意给定的K(<N),输出扫描完第K遍后的中间结果序列。
输入格式:
输入在第1行中给出N和K(1≤K<N≤100),此后N行,每行包含一个长度不超过10的、仅由小写英文字母组成的非空字符串。
输出格式:
输出冒泡排序法扫描完第K遍后的中间结果序列,每行包含一个字符串。
输入样例:
6 2
best
cat
east
a
free
day
输出样例:
best
a
cat
day
east
free
代码
#include<bits.stdc++.h>
using namespace std;
int main()
{
int n,k;
cin>>n>>k;
char a[120][12],b[120];
for(int i=0;i<n;i++){
cin>>a[i];
}
for(int i=0;i<k;i++){
for(int j=0;i<n-1-i;j++){
if(strcmp(a[j],a[j+1])>0){ //用strcmp()函数比较字符串的大小
strcpy(b,a[j]); //用strcpy()函数把a[j]复制给b
strcpy(a[j],a[j+1]);
strcpy(a[j+1],b);
}
}
}
for(int i=0;i<n;i++){
cout<<a[i]<<"\n";
}
return 0;
}
3.两个有序链表序列的交集
已知两个非降序链表序列S1与S2,设计函数构造出S1与S2的交集新链表S3。
输入格式:
输入分两行,分别在每行给出由若干个正整数构成的非降序序列,用−1表示序列的结尾(−1不属于这个序列)。数字用空格间隔。
输出格式:
在一行中输出两个输入序列的交集序列,数字间用空格分开,结尾不能有多余空格;若新链表为空,输出NULL。
输入样例:
1 2 5 -1
2 4 5 8 10 -1
输出样例:
2 5
代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a[99999999],a1=0,a2=0,b[99999999],b1=0,b2=0;
while(cin>>n&&n!=-1) a[a1++]=n;
while(cin>>n&&n!=-1) b[b1++]=n;
int flag=0;
while(a1!=a2&&b1!=b2){
if(a[a2]==b[b2]){
if(flag) cout<<" ";
flag=1;
cout<<a[a2];
a2++; b2++;
}
else{
if(a[a2]<b[b2]) a2++;
else b2++;
}
}
if(flag==0) cout<<"NULL";
return 0;
}
871

被折叠的 条评论
为什么被折叠?



