1、顺序表(线性表的顺序存储结构)
#include<stdio.h> #include<stdlib.h> const int N = 1000; typedef int Position;
// 数组下标 typedef struct LNode *PtrToLNode; // PtrToLNode是结构体指针,指向结构体类型 struct
LNode // 节点表示的东西 { int Data[N]; // 数据 Position Last; // 位置 }; typedef
PtrToLNode List; List L; // 定义顺序表(线性表) //初始化 List init() { List L; L =
(List)malloc(sizeof(struct LNode)); L->Last = -1; return L; } //创建表 void
create(List L) { int n; scanf("%d",&n); L->Last = n - 1; for(int i = 0 ; i <=
L->Last ; i ++) scanf("%d",&L->Data[i]); } //查找 //last表示(n -
1),线性表的长度可以通过L->Last + 1得到 Position Find(List L, int x) // 从线性表L中找x { Position
i = 0; // 下标为i的元素可以通过L->Data[i]访问 while(i <= L->Last && L->Data[i] != x) i ++;
if(i > L->Last) return -1; else return i; // 返回下标 } //插入
//在L的指定位序i前插入一个新元素X;位序i元素的数组位置下标是i-1 bool Insert( List L, int x, int i ) {
Position j; //表满 if ( L->Last == N - 1) return false;
//检查插入位序的合法性:是否在1~n+1。n为当前元素个数,即Last+1 if ( i<1 || i>L->Last+2 ) return false;
//将位序i及以后的元素顺序向后移动 for(j = L->Last ; j >= i - 1 ; j --) L->Data[j + 1] =
L->Data[j]; L->Data[i - 1] = x; // 新元素插入第i位序,其数组下标为i-1 L->Last ++; //
使Last仍指向最后元素 return true; } // 删除元素 bool Delete( List L, int i ) { /*
从L中删除指定位序i的元素,该元素数组下标为i-1 */ Position j; //不合理 if( i < 1 || i > L->Last + 1 )
return false; //将位序i+1及以后的元素顺序向前移动 for(j = i ; j <= L->Last ; j ++) L->Data[j -
1] = L->Data[j]; L->Last --; //使Last仍指向最后元素 return true; } //输出表 void
Printf(List L) { for(int i = 0 ; i <= L->Last ; i ++) printf("%d ",L->Data[i]);
} int main() { List l; l = init(); create(l); Printf(l); return 0; }

2、单链表(线性表的链式存储结构)

写在前面:

1、指针
int a; // 定义一个整形变量a int *p; // 定义一个整型指针变量p

(1)定义:

定义一个指针,只需要在变量前面加一个*号就OK啦。

(2)作用:

存储一个地址。确切地说是存储一个内存空间的地址,比如说整型变量a的地址。严格地说这里地指针p也只能存放”一个存放整数地内存空间“的地址,因为在定义的时候我们已经限制了这一(即定义的时候*p的前面是int)。

(3)如何存储地址:
p = &a;
这样整型指针p就获得了(存储了)整型变量a的地址,我们可以形象地理解整型指针p指向了整型变量a。

2、动态分配内存函数:malloc()

        回想一下,我们想在程序中存储一个整数10,除了使用int a;这种方式在内存中申请一块区域来存储,还有另一种动态存储方法。
malloc(4);
       
malloc函数的作用就是从内存中申请分配指定字节大小的内存空间。上面这行代码就申请了4个字节。如果你不知道int类型是4个字节的,还可以使用sizeof(int)获取int类型所占的字节数,如下:
malloc(sizeof(int));
       
哦现在你已经成功地从内存中申请了4个字节地空间来准备存放一个整数,可是如何来队这个空间操作呢?这里我们就需要用一个指针来指向这个空间,即存储这个空间的首地址。
int *p; p = (int *)malloc(sizeof(int));
        需要注意,malloc函数的放回类型的是void *类型。void *表示未确定类型的指针。在C和C++中,void
*类型可以强制转换为任何其他类型的指针。上面代码中我们将其强制转化为整型指针,以便告诉计算机这里的4个字节作为一个整体用来存放整数。还记得我们之前遗留了一个问题:指针是用来存储内存地址的,为什么要分不同类型的指针呢?因为指针变量存储的是一个内存空间的首地址(第一个字节的地址),但是这个空间占用了多少个字节,用来存储什么类型的数,则是由指针的类型来标明的。这样系统才知道应该取多少个连续内存作为一个数据。

       OK,现在我们可以通过指针p对刚才申请的4个字节的空间进行操作了,例如我们向这个空间中存入整数10,如下:
*p = 10;
       完整代码如下:
#include<bits/stdc++.h> using namespace std; int main() { int *p; // 定义一个指针p p
= (int *)malloc(sizeof(int)); // 指针p获取动态分配的内存空间地址 *p = 10; // 向指针p所指向的内存空间中存入10
printf("%d",*p); return 0; }
       
到这里你可能要问:为什么要用这么复杂的办法来存储数据呢?因为之前的方法,我们必须预先准确地知道所需变量地个数,也就是我们必须所有的变量。比如我们定义了100个整型变量,那么程序就只能存储100个整数,如果现在的实际情况是需要存储101个,那必须修改程序才可以。如果有一天你写的软件已经发布或者交付使用,却发现要存储1000个数才行,那就不得不再次修改程序,重新编译程序,发布一个新版本来代替原来的。而有了malloc函数我们便可以在程序运行的过程中根据实际情况申请空间。

3、链表的创建

     
每一个结点都由两个部分组成。左边的部分用来存放具体的数值,那么用一个整型变量就可以;右边的部分需要存储下一个结点的地址,可以用指针来实现(也称为后继指针)。

这里我们定义一个结构体类型来存储这个结点,如下:
struct node { int data; struct node *next; };

      
上面代码中,我们定义了一个叫做node的结构体类型,这个结构体类型有两个成员。第一个成员是整型data,用来存储具体的数值;第二个成员是一个指针,用来存储下一个结点的地址。因为下一个结点的类型也是struct
node,所以这个指针的类型也必须是struct node *p类型的指针。

         如何建立链表呢?首先我们需要一个头指针head指向链表的最开始。当链表还没有建立的时候头指针head为空(也可以理解为指向空结点)。
struct node *head; head = NULL; // 头指针初始为空
         现在我们来创建第一个结点,并用临时指针p指向这个结点。
struct node *p; // 定义一个临时指针 p = (struct node *)malloc(sizeof(struct node)); //
动态申请一个空间,用来存放一个结点,并用临时指针p指向这个结点
         接下来分别设置新创建的第一个结点的左半部分和右半部分。
scanf("%d", &a); p -> data = a; // 将数据存储到当前结点的data域中 p -> next = NULL; //
设置当前的后继指针指向空,也就是当前结点的下一个结点为空
       下面来设置头指针并设置新创建结点的 *next指向空。头指针的作用是方便以后从头遍历整个链表。
if(head == NULL) head = p; // 如果这是第一个创建的结点,则将头指针指向这个结点 else q -> next = p; //
如果不是第一个创建的结点,则将上一个结点的后继指针,则将上一个结点的后继指针指向当前结点
         如果这是第一个创建的结点,则将头指针指向这个结点。

          如果不是第一个创建的结点,则将上一个结点的后继指针指向当前结点。

最后要将指针q也指向当前结点,因为待会儿临时指针p将会指向新创建的结点

完整代码如下:
#include<bits/stdc++.h> using namespace std; // 这里创建一个结构体用来表示链表的结点类型 struct
node { int data; struct node *next; }; int main() { int n; scanf("%d",&n);
struct node *head; head = NULL; // 头指针初试为空,代表还没建表 for(int i = 1 ; i <= n ; i
++) { int a; scanf("%d",&a); struct node *q; struct node *p; // 定义临时指针p p =
(struct node *)malloc(sizeof(struct node)); // 动态申请一个空间,用来存放一个结点,并用临时指针p指向这个结点
p -> data = a; p -> next = NULL; // 设置当前结点的后继指针指向空,也就是当前的下一个结点为空 if(head ==
NULL) // 头指针是初试状态,代表还没建结点 { head = p; // 如果这是第一个创建的结点,则将头指针指向这个结点 } else { q ->
next = p; // 如果不是第一个创建的结点,则将上一个结点的后继指针指向结点 } q = p; // q也指向当前结点,因为临时指针p会往后移动 }
// 输出链表的所有数 struct node *t; t = head; while(t != NULL) { printf("%d ",t ->
data); t = t -> next; } return 0; } //输入8 1 2 3 4 5 7 8 9 //输出 1 2 3 4 5 7 8 9

建表的方式:

(1)头插法建表:

代码:
#include<bits/stdc++.h> using namespace std; const int N = 1e4 + 10; int a[N];
int n; // 元素数量 typedef struct LNode *PtrToLNode; struct LNode { int data;
PtrToLNode next; }; typedef PtrToLNode Position; // 这里的位置是结点的地址 typedef
PtrToLNode List; // 头插法建表 List CreateFromHead() { List L; Position s; // 临时指针s
L = (List)malloc(sizeof(struct LNode)); // 头指针L指向一块结点空间(头节点) L -> next = NULL;
scanf("%d",&n); for(int i = 0 ; i < n ; i ++) { s = (PtrToLNode)malloc(sizeof
(struct LNode)); scanf("%d",&a[i]); s -> data = a[i]; s -> next = L -> next; L
-> next = s; } return L; } int lengthL(List L) { Position p; int cnt = 0; //
计数器 p = L -> next; // 头节点不存数据,算第0个结点,从第一个结点开始算 while(p != NULL) { p = p ->
next; cnt ++; // 当前p指向的是第cnt个结点 } return cnt; } // 输出链表 void printfL(List L) {
Position p; p = L -> next; // 头结点不存数据,从第一个结点开始遍历,头节点是相当于是第0个结点 while(p != NULL)
{ printf("%d ",p -> data); p = p -> next; } } int main() { List l; //
可以用List定义具体的表头结点指针,该指针就代表了一个链式表 // 建表 l = CreateFromHead(); // 输出表的长度 int len =
lengthL(l); printf("%d\n",len); // 输出刚刚建的表 printfL(l); return 0; }
运行结果:

(2)正向建表(尾插法,下面有注释,能懂)

代码:
#include<bits/stdc++.h> using namespace std; const int N = 1e4 + 10; int a[N];
int n; // 元素数量 typedef struct LNode *PtrToLNode; struct LNode { int data;
PtrToLNode next; }; typedef PtrToLNode Position; // 这里的位置是结点的地址 typedef
PtrToLNode List; // 头插法建表 List CreateFromHead() { List L; Position s; // 临时指针s
L = (List)malloc(sizeof(struct LNode)); // 头指针L指向一块结点空间(头节点) L -> next = NULL;
scanf("%d",&n); for(int i = 0 ; i < n ; i ++) { s = (PtrToLNode)malloc(sizeof
(struct LNode)); scanf("%d",&a[i]); s -> data = a[i]; s -> next = L -> next; L
-> next = s; } return L; } // 尾插法建表 List CreateFromTail() /*尾插法建表*/ { List L;
Position r,s; char c; int flag = 1; // 输入结束标志 L =
(List)malloc(sizeof(LNode));//为头结点分配存储空间 L->next = NULL; r = L;
//r指针始终动态指向链表的当前表尾,尾插法插入很方便,只需要修改一个指针 printf("请输入数字,以-1结束:\n"); while(flag ==
1) //flag为标志,初值为1。输入“$”时flag为0,建表结束 { int x; scanf("%d",&x); if(x != -1) { s =
(PtrToLNode)malloc(sizeof(LNode)); s->data = x; r->next = s; // 尾插法只需要修改一个指针 r
= s; // 更新尾结点,为下一次插入做准备 } else { flag = 0; r->next = NULL; } } return L; } int
lengthL(List L) { Position p; int cnt = 0; // 计数器 p = L -> next; //
头节点不存数据,算第0个结点,从第一个结点开始算 while(p != NULL) { p = p -> next; cnt ++; //
当前p指向的是第cnt个结点 } return cnt; } // 输出链表 void printfL(List L) { Position p; p = L
-> next; // 头结点不存数据,从第一个结点开始遍历,头节点是相当于是第0个结点 while(p != NULL) { printf("%d ",p
-> data); p = p -> next; } } int main() { List l; //
可以用List定义具体的表头结点指针,该指针就代表了一个链式表 // 建表 l = CreateFromTail(); // 输出表的长度 int len =
lengthL(l); printf("%d\n",len); // 输出刚刚建的表 printfL(l); return 0; }

       

技术
下载桌面版
GitHub
Gitee
SourceForge
百度网盘(提取码:draw)
云服务器优惠
华为云优惠券
腾讯云优惠券
阿里云优惠券
Vultr优惠券
站点信息
问题反馈
邮箱:[email protected]
吐槽一下
QQ群:766591547
关注微信