数据结构-单链表-结构体定义
单链表,用于存储逻辑关系为 "一对一" 的数据,与顺序表不同,链表不限制数据的物理存储状态,换句话说,使用链表存储的数据元素,其物理存储位置是随机的。
结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻。
例如{1,2,3}:
线性表的链式表示又称为非顺序映像或链式映像。
各结点由两个域组成:
数据域:存储元素数值数据
指针域:存储直接后继结点的存储位置
头指针是指向链表中第一个结点的指针
首元结点是指链表中存储第一个数据元素a1的结点
头结点是在链表的首元结点之前附设的一个结点;数据域内只放空表标志和表长等信息
如何表示空表?
答:有头结点时,当头结点的指针域为空时表示空表
在链表中设置头结点有什么好处?
答:⒈便于首元结点的处理
首元结点的地址保存在头结点的指针域中,所以在链表的第一个位置上的操作和其它位置一致,无须进行特殊处理;
⒉便于空表和非空表的统一处理
无论链表是否为空,头指针都是指向头结点的非空指针,因此空表和非空表的处理也就统一了。
头结点的数据域内装的是什么?
答:头结点的数据域可以为空,也可存放线性表长度等附加信息,但此结点不能计入链表长度值。
链表(链式存储结构)的特点:
1)结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻
2)访问时只能通过头指针进入链表,并通过每个结点的指针域向后扫描其余结点,所以寻找第一个结点和最后一个结点所花费的时间不等
这种存取元素的方法被称为顺序存取法
链表的优缺点:
优点:数据元素的个数可以自由扩充
插入、删除等操作不必移动数据,只需修改链接指针,修改效率较高链表的优缺点
缺点:存储密度小
存取效率不高,必须采用顺序存取,即存取数据元素时,只能按链表的顺序进行访问(顺藤摸瓜)
单链表是由表头唯一确定,因此单链表可以用头指针的名字来命名
若头指针名是L,则把链表称为表L
用c++描述的单链表大致可以分两种,一种用结构体定义,一种用类定义;
链表也可以分为头插法和尾插法
头插法
生成新结点
将读入数据存放到新结点的数据域中
将该新结点插入到链表的前端
1 #include<iostream> 2 using namespace std; 3 4 typedef struct SingLinkList { 5 int data; //数据域 6 struct SingLinkList *next; //指针域 7 }SLL,*Slist; //相当于struct SingLinkList,*Slist相当于struct SingLinkList * 8 9 //新建链表,确认链表的长度 10 void New_LinkList(Slist L,int n) //Slist L相当于struct SingLinkList * L 11 { 12 int i; 13 int v; 14 Slist p; 15 L->next = NULL; 16 17 //这是用前插法创建链表,此时就不需要使用尾结点了 18 //所以遍历的结果是与输入值的顺序是相反的 19 for (int i = n; i > 0; i--) 20 { 21 p = (Slist)malloc(sizeof(SLL)); 22 cin >> v; 23 p->data = v; 24 p->next = L->next; //p的指针域指向L的指针域,即p指向空, 25 L->next = p; //但是此时只有p这个结点,还需要头结点指向p 26 //此时头结点指向p,而p指针域指向最后, 27 //可以认为p是尾结点,它的指针域自然要指向空了。 28 //然后你需要几个结点就继续按照原来的一直干下去。记住这是前插法。 29 //单链表的结点不像数组可以通过下标找到,它得从头开始找。 30 //每个节点都与上一个结点相连没有名字是地址传地址 31 } 32 } 33 34 // 获取链表中指定位置的数据元素 35 int GetElem_LinkList( Slist L, int i, int *e) 36 { 37 Slist p; 38 p = L->next; 39 int j = 1; 40 41 while (p && j < i) 42 { 43 p = p->next; 44 ++j; 45 } 46 47 if (!p || j > i) 48 { 49 return 0; 50 } 51 52 *e = p->data; 53 return 1; 54 } 55 56 // 在链表中指定位置插入数据 57 int Isert_LinkList(Slist L, int i, int* e) 58 { 59 Slist p, s; 60 p = L; 61 int j = 0; 62 63 while (p && j < i - 1) 64 { 65 p = p->next; 66 ++j; 67 } 68 69 if (!p || j > i - 1) 70 { 71 return 0; 72 } 73 74 s = (Slist)malloc(sizeof(SLL)); 75 s->data = *e; 76 s->next = p->next; 77 p->next = s; 78 79 return 1; 80 } 81 82 // 删除链表中指定数据元素 83 int Delete_LinkList(Slist L, int i, int* e) 84 { 85 Slist p, q; 86 p = L; 87 int j = 0; 88 89 while (p->next && j < i - 1) 90 { 91 p = p->next; 92 ++j; 93 } 94 95 if (!(p->next) || j > i - 1) 96 { 97 return 0; 98 } 99 100 101 q = p->next; 102 p->next = q->next; 103 *e = q->data; 104 105 free(q); 106 return 1; 107 } 108 109 // 显示链表元素的内容 110 int Print_LinkList(Slist L, char* s) 111 { 112 Slist p; 113 114 p = L->next; 115 if (p == NULL) 116 { 117 cout << "该链表为空!" << endl; 118 return 0; 119 } 120 121 while (p != NULL) 122 { 123 cout << p->data << " "; 124 p = p->next; 125 } 126 cout << endl; 127 return 1; 128 } 129 130 int main() 131 { 132 // 创建并初始化链表 133 SLL L; 134 cout << "请输入五个节点的数据" << endl; 135 New_LinkList(&L, 5); 136 Print_LinkList(&L, (char*)"初始化链表:"); 137 138 int s, v; 139 140 // 插入数据 141 cout << "请输入数据插入的位置和值:"; 142 cin >> s >> v; 143 Isert_LinkList(&L, s, &v); 144 Print_LinkList(&L, (char*)"插入后的节点数:"); 145 146 147 // 查询数据 148 cout << "请输入数据查询的位置:"; 149 cin >> s; 150 GetElem_LinkList(&L, s, &v); 151 cout << "第" << s << "个数据是:" << v << endl; 152 153 // 删除出数据 154 cout << "请输入数据删除的位置:"; 155 cin >> s; 156 if (Delete_LinkList(&L, s, &v)) 157 { 158 cout << "数据删除成功,你所删除的数据是:" << v << endl; 159 } 160 else 161 { 162 cout << "数据删除失败!" << endl; 163 } 164 Print_LinkList(&L, (char*)"删除后的链表:"); 165 166 167 return 0; 168 }
尾插法
从一个空表L开始,将新结点逐个插入到链表的尾部,尾指针r指向链表的尾结点。
初始时,r同L均指向头结点。每读入一个数据元素则申请一个新结点,将新结点插入到尾结点后,r指向新结点。
1 #include <iostream> 2 using namespace std; 3 4 #define ERROR 0 5 #define OK 1 6 7 8 // 链表节点的结构体 9 typedef struct LNode 10 { 11 int data; // 数据域 12 struct LNode* next; // 指针域 13 }SLL , * SLNode; 14 15 16 // 新建链表,确认链表的长度 17 void New_LinkList(SLNode L, int n) 18 { 19 int i; 20 int v; 21 22 SLNode p,r; // 新建单链表 23 L->next = NULL; 24 r = L; //尾指针r指向头节点 25 26 //链表的尾插法 27 for (int i = n; i > 0; i--) 28 { 29 p = (SLNode)malloc(sizeof(SLL)); //生成新结点 30 cin >> v; 31 p->data = v; //输入元素值 32 p->next = NULL; r->next = p; //插入到表尾 33 r = p; //r指向新的尾结点 34 //插入的结点p是尾结点,所以要使r指向p,才能让再次插入的 35 //结点顺利执行第31行。 36 } 37 } 38 39 40 // 获取链表中指定位置的数据元素 41 int GetElem_LinkList(SLNode L, int i, int *e) 42 { 43 SLNode p; 44 p = L->next; 45 int j = 1; 46 47 while (p && j < i) 48 { 49 p = p->next; 50 ++j; 51 } 52 53 if (!p || j > i) 54 { 55 return ERROR; 56 } 57 58 *e = p->data; 59 return OK; 60 } 61 62 63 // 在链表中指定位置插入数据 64 int Isert_LinkList(SLNode L, int i, int* e) 65 { 66 SLNode p, s; 67 p = L; 68 int j = 0; 69 70 while (p && j < i - 1) 71 { 72 p = p->next; 73 ++j; 74 } 75 76 if (!p || j > i - 1) 77 { 78 return ERROR; 79 } 80 81 s = (SLNode)malloc(sizeof(SLL)); 82 s->data = *e; 83 s->next = p->next; 84 p->next = s; 85 86 return OK; 87 } 88 89 90 // 删除链表中制定数据元素 91 int Delete_LinkList(SLNode L, int i, int* e) 92 { 93 SLNode p, q; 94 p = L; 95 int j = 0; 96 97 while (p->next && j < i - 1) 98 { 99 p = p->next; 100 ++j; 101 } 102 103 if (!(p->next) || j > i - 1) 104 { 105 return ERROR; 106 } 107 108 109 q = p->next; 110 p->next = q->next; 111 *e = q->data; 112 113 free(q); 114 return OK; 115 } 116 117 118 // 显示链表元素的内容 119 int Print_LinkList(SLNode L, char* s) 120 { 121 SLNode p; 122 123 p = L->next; 124 if (p == NULL) 125 { 126 cout << "该链表为空!" << endl; 127 return ERROR; 128 } 129 130 while (p != NULL) 131 { 132 cout << p->data << " "; 133 p = p->next; 134 } 135 cout << endl; 136 return OK; 137 } 138 139 140 int main() 141 { 142 // 创建并初始化链表 143 struct LNode L; 144 cout << "请输入五个节点的数据" << endl; 145 New_LinkList(&L, 5); 146 Print_LinkList(&L, (char*)"初始化链表:"); 147 148 int s, v; 149 150 // 插入数据 151 cout << "请输入数据插入的位置和值:"; 152 cin >> s >> v; 153 Isert_LinkList(&L, s, &v); 154 Print_LinkList(&L, (char*)"插入后的节点数:"); 155 156 157 // 查询数据 158 cout << "请输入数据查询的位置:"; 159 cin >> s; 160 GetElem_LinkList(&L, s, &v); 161 cout << "第" << s << "个数据是:" << v << endl; 162 163 // 删除出数据 164 cout << "请输入数据删除的位置:"; 165 cin >> s; 166 if (Delete_LinkList(&L, s, &v)) 167 { 168 cout << "数据删除成功,你所删除的数据是:" << v << endl; 169 } 170 else 171 { 172 cout << "数据删除失败!" << endl; 173 } 174 Print_LinkList(&L, (char*)"删除后的链表:"); 175 176 177 return 0; 178 }