链表 链表是线性表的链式存储方式,逻辑上相邻的数据在计算机中的内存位置不必须相邻,给每一个元素 加一个指针域,指向下一个元素的位置。
如下图所示 :
链表的核心要素 :
每个结点由数据域和指针域组成
指针域指向下一个结点的内存地址
单链表 链表的结点均单项指向下一个结点,形成一条单项访问的数据链。
相关接口实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 typedef int DataType;typedef struct LinkNode { DataType data; struct LinkNode * next ; }LinkNode,LinkList; bool initLinkList (LinkList*& linklist) { linklist = new LinkList; if (!linklist) { return false ; } linklist->next = NULL ; return true ; } bool pushFront (LinkList*& L, LinkNode* node) { if (!L || !node) { return false ; } node->next = L->next; L->next = node; return true ; } bool pushBack (LinkList*& L, LinkNode* node) { if (!L || !node) { return false ; } LinkNode* tempnode = L; while (tempnode->next) { tempnode = tempnode->next; } tempnode->next = node; node->next = NULL ; return true ; } bool insertLinkList (LinkList*& L,int index,DataType num) { if (!L || index <= 0 ) { return false ; } if (index == 1 ) { LinkNode* newnode = new LinkNode; newnode->data = num; pushFront(L, newnode); return true ; } int count = 0 ; LinkList* p, * newNode; p = L; while (p && count < index-1 ) { p = p->next; count++; } newNode = new LinkNode; newNode->data = num; newNode->next = p->next; p->next = newNode; return true ; } bool getElems (LinkList*& L, int postion,int & e) { if (!L || postion <= 0 || !L->next) { return false ; } int count = 0 ; LinkList* tempnode = L; while (count < postion && tempnode) { count++; tempnode = tempnode->next; } if (!tempnode) { return false ; } e = tempnode->data; return true ; } bool findElems (LinkList*& L, int e,int &index) { if (!L || !L->next) { return false ; } LinkList* tempnode = L; int count = 0 ; while (tempnode && tempnode->data != e) { tempnode = tempnode->next; count++; } if (!tempnode) { return false ; } index = count; return true ; } bool deleteElems (LinkList*& L, int index) { if (!L || !L->next || index <= 0 ) { return false ; } int count = 0 ; LinkNode* tempnode = L; while (tempnode && count != index-1 ) { count++; tempnode = tempnode->next; } if (!tempnode->next) { return false ; } LinkNode* removeNode = tempnode->next; tempnode->next = removeNode->next; delete removeNode; return true ; } bool destoryList (LinkList*& L) { LinkList* tempnode = L; while (tempnode) { L = L->next; delete tempnode; tempnode = L; } return true ; } void printLinkList (LinkList*& L) { LinkNode* p = NULL ; if (!L) { return ; } p = L->next; while (p) { cout << p->data <<" " ; p = p->next; } cout << endl ; }
循环链表 在单链表的基础上让最后一个结点指向第一个结点。
相关接口实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 typedef int DataType;typedef struct LinkNode { DataType data; struct LinkNode * next ; }LinkNode, LinkList; bool inintLinkList (LinkList*& L) { L = new LinkList; if (!L) { return false ; } L->next = L; L->data = -1 ; return true ; } bool pushBack (LinkList*& L,LinkNode *node) { if (!L || !node) { return false ; } if (L == L->next) { L->next = node; node->next = L; } LinkNode* tempnode = NULL ; tempnode = L->next; while (tempnode->next != L) { tempnode = tempnode->next; } tempnode->next = node; node->next = L; return true ; } bool pushFront (LinkList*& L, LinkNode* node) { if (!L || !node) { return false ; } LinkNode* tempnode = L->next; L->next = node; node->next = tempnode; return false ; } void printList (LinkNode*& L) { if (!L || L == L->next) { return ; } LinkNode* tempnode = NULL ; tempnode = L->next; while (tempnode != L) { cout << tempnode->data << " " ; tempnode = tempnode->next; } cout << endl ; }
约瑟夫环问题 定一个数,从头开始依次报数,报道这个数的就delete,直到求出最后一个出局的编号是多少。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 bool Joseph (LinkNode*& L,int val) { LinkNode* p, * q; p = L; if (!L || p->next == L) { return false ; } if (val < 1 ) { return false ; } int i = 0 ; int j = 0 ; int times = 0 ; int num = 0 ; do { i += val; while (p->next) { if (p->next != L) { j++; } if (j >= i) { break ; } p = p->next; } times++; q = p->next; num = q->data; p->next = q->next; delete q; printList(L); } while (L->next != L); cout << "最后被删除的结点位置是第" << num << "个" << endl ; return true ; }
双向链表 在单链表的基础上添加一个指向前一个结点的指针。
相关接口实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 typedef int dataType;typedef struct doubleNode { dataType data; struct doubleNode * prev ; struct doubleNode * next ; }LinkNode,LinkList; bool initLinkList (LinkList*& L) { L = new LinkList; if (!L) { return false ; } L->next = NULL ; L->prev = NULL ; L->data = -1 ; return true ; } bool pushBack (LinkList*& L,LinkNode*& node) { if (!L || !node) { return false ; } LinkList* tempnode = NULL ; tempnode = L; while (tempnode->next) { tempnode = tempnode->next; } tempnode->next = node; node->prev = tempnode; node->next = NULL ; return true ; } bool pushFront (LinkList* &L, LinkNode* &node) { if (!L || !node) { return false ; } if (L->next == NULL ) { L->next = node; node->next = NULL ; node->prev = L; } else { LinkNode* tempnode = NULL ; tempnode = L->next; L->next = node; node->prev = L; node->next = tempnode; tempnode->prev = node; } return true ; } void printLinkList (LinkList*& L) { if (!L || !L->next) { return ; } LinkList* tempnode = NULL ; tempnode = L->next; while (tempnode) { cout << tempnode->data << " " ; tempnode = tempnode->next; } cout << endl ; } bool insertLinkList (LinkList*& L,int index,int num) { if (!L || !L->next || index < 1 ) { return false ; } int count = 1 ; LinkNode* tempnode = NULL ; tempnode = L->next; while (tempnode) { if (count == index) { break ; } tempnode = tempnode->next; count++; } if (!tempnode) { return false ; } LinkNode* newnode = new LinkNode; newnode->data = num; LinkNode* tempnodeII = tempnode->prev; tempnodeII->next = newnode; newnode->prev = tempnodeII; newnode->next = tempnode; tempnode->prev = newnode; return true ; } bool deleteLinkList (LinkList*& L, int index) { if (!L || !L->next || index < 1 ) { return false ; } int count = 1 ; LinkNode* tempnode = NULL ; tempnode = L->next; while (tempnode) { if (index == count) { break ; } count++; tempnode = tempnode->next; } if (!tempnode->next) { tempnode->prev->next = NULL ; delete tempnode; } else { tempnode->prev->next = tempnode->next; tempnode->next->prev = tempnode->prev; delete tempnode; } return true ; } bool getElems (LinkList*& L, int index, int & e) { if (!L || !L->next || index < 1 ) { return false ; } int count = 1 ; LinkNode* tempnode = NULL ; tempnode = L->next; while (tempnode) { if (count == index) { break ; } tempnode = tempnode->next; count++; } if (!tempnode) { return false ; } e = tempnode->data; return true ; } void destoryLinkList (LinkList*& L) { LinkNode* tempnode = NULL ; tempnode = L; while (tempnode) { L = L->next; delete tempnode; tempnode = L; } }
实际应用 Linux内核共享双向链表
在 linux 内核中,有大量的数据结构需要用到双向链表,例如进程、文件、模块、页面等。
若采用双向链表的传统实现方式,需要为这些数据结构维护各自的链表,并且为每个链表都
要设计插入、删除等操作函数。因为用来维持链表的 next 和 prev 指针指向对应类型的对
象,因此一种数据结构的链表操作函数不能用于操作其它数据结构的链表。
有没有一种方式让多个链表共享同一套链表操作呢?
——将结点中的指针域和数据域分离 。
例如 :
1 2 3 4 5 6 7 8 9 10 11 12 typedef struct LinkNode { struct LinkNode * next ; struct LinkNode * prev ; }LinkNode; typedef struct { int fd; time_t timeout; LinkNode node; }ConnTimeOut;
特殊访问 :
——取到结构体中挂件的的偏移量,得到结构体的地址,然后进行访问。
1 2 3 4 5 6 ConnTimeOut* ct = new ConnTimeOut; ct->fd = 3 ; LinkNode* p = &(ct->node); int offset = offsetof(ConnTimeOut, node);ConnTimeOut* tmp = (ConnTimeOut*)((size_t )p - offset); cout <<tmp->fd;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 bool initLinkNode (LinkNode& L) { L.next = NULL ; L.prev = NULL ; return true ; } int main (void ) { ConnTimeOut* cL = NULL ; cL = new ConnTimeOut; cL->fd = -1 ; initLinkNode(cL->node); return 0 ; }
怎么个共享法呢?
重新解释 :
将一个结点中的指针域剥离出来,创建一个新的结构体,来存放这个指针域,也就是说结构体嵌套。不同的结点(结构体数据内容不同,但是都有这个剥离出来的指针域。),使用同一个接口进行操作,靠的就是他们中相同的”指针域”结构体,就是对这个结构体中的”指针域结构体”进行操作。
例如 : 不同的结构体,但是但是他们都有上面LinkNode node这个挂件,那么他们就共用同一个初始化指针指向的函数。
传递这个共有的内容给函数,这个函数对他们进行操作。
例如: 将结点链接起来。不同链表使用同一个函数进行插入操作。