一、线性结构:
数组:
(1) 一片连续的内存空间 不能是断开的
(2) 因为是连续的内存空间 才可以使用下标来访问
arr[i] === *(arr+i)
访问效率非常高
(3) 在数据前面插入和删除的效率都比较低 (需要移动数据)
链表:
(1) 内存是不连续的
(2) 访问效率比较低 因为需要从第一个元素开始遍历
(3) 在任何地方插入和删除的效率都比较高 (只需要改变指针的值)
二、单向链表
单向链表的节点:节点元素+下一个节点的地址
1 typedef int T; 2 //单向链表的节点 3 typedef struct SNode{ 4 T data; //节点元素 5 struct SNode *next; //下一个节点的地址 指向下一个节点 6 }SNode; 7 8 //定义单向链表类型 SLink 9 typedef struct SNode * SLink;
1.初始化链表
申请头节点的动态内存 头节点不保存数据 也没有下一个节点的地址(NULL)
1 void init(SLink* plink){ 2 *plink = malloc(sizeof(SNode)); 3 (*plink)->next = NULL; 4 } 5 SLink createSLink(){ 6 SNode * node = malloc(sizeof(SNode)); 7 node->next = NULL; 8 return node; 9 }
2.判断链表是否为空
1 bool isEmpty(SLink link){ 2 return link->next == NULL; 3 }
3.在头部插入一个节点 将要存储的数据加入
1 void insertFront(SLink link,T data){ 2 SNode *node = malloc(sizeof(SNode)); 3 node->data = data; 4 node->next = link->next; 5 link->next = node; 6 }
4.遍历链表
1 void travel(SLink link){ 2 if(link != NULL){ 3 SNode *node = link->next; 4 while(node != NULL){ 5 printf("%d ",node->data); 6 node = node->next; 7 } 8 printf(" "); 9 } 10 }
5.链表数据个数
1 size_t size(SLink link){ 2 SNode *node = link->next; 3 size_t cnt = 0; 4 while(node != NULL){ 5 cnt++; 6 node = node->next; 7 } 8 return cnt; 9 }
6.获得index下标位置节点的前一个节点
1 static SNode *getPrevNode(SLink link,size_t index){ 2 SNode *node = link; 3 for(int i=0;i<index;i++){ 4 node = node->next; 5 } 6 return node; 7 }
7.在指定的下标位置插入一个元素
1 void insert(SLink link,size_t index,T data){ 2 if(index>size(link)){ 3 return; 4 } 5 //找到index下标节点的前一个节点 6 SNode *prevNode = getPrevNode(link,index); 7 SNode *currNode = malloc(sizeof(SNode)); 8 currNode->data = data; 9 currNode->next = prevNode->next; 10 prevNode->next = currNode; 11 }
8.根据下标删除元素
1 void deleteByIndex(SLink link,size_t index){ 2 if(index>=size(link)){ 3 return; 4 } 5 SNode *prevNode = getPrevNode(link,index); 6 SNode *currNode = prevNode->next; 7 prevNode->next = prevNode->next->next; 8 free(currNode);//释放内存 9 }
9.清空链表中的元素
1 void clear(SLink link){ 2 SNode *node = link->next; 3 while(node != NULL){ 4 SNode *next = node->next;//记录当前节点的后一个节点位置 5 free(node);//释放当前节点的内存 6 node = next;//指向下一个节点 7 } 8 link->next = NULL; 9 }
10.销毁链表
1 void destroy(SLink *plink){ 2 clear(*plink); 3 free(*plink); 4 *plink = NULL; 5 }
11.删除链表中第一个数据是data的节点
1 void deleteData(SLink link,T data){ 2 SNode *prevNode = link; 3 SNode *currNode = link->next; 4 while(currNode != NULL){ 5 if(currNode->data == data){ 6 prevNode->next = currNode->next; 7 free(currNode); 8 return; 9 } 10 prevNode = currNode; 11 currNode = currNode->next; 12 } 13 }
12.删除链表中所有数据是data的节点
1 void deleteAllDatas(SLink link,T data){ 2 SNode *prevNode = link;//头节点 3 SNode *currNode = link->next; 4 while(currNode != NULL){ 5 if(currNode->data == data){ 6 prevNode->next = currNode->next; 7 free(currNode); 8 currNode = prevNode->next; 9 continue; 10 } 11 prevNode = currNode; 12 currNode = currNode->next; 13 } 14 }
13.根据下标修改链表中的元素
1 void modify(SLink link,size_t index,T data){ 2 if(index>=size(link)){ 3 return; 4 } 5 SNode *node = getPrevNode(link,index+1); 6 node->data = data; 7 }
14.根据下标获取元素
如果index>=size(link)都将引发错误
1 T getDataByIndex(SLink link,size_t index){ 2 /* 3 if(index>=size(link)){ 4 return -1; 5 } 6 */ 7 SNode *node = link->next; 8 for(int i=0;i<index;i++){ 9 node = node->next; 10 } 11 return node->data; 12 }
15.链表逆序 ---笔试题概率最大的编程题
1 void reverse(SLink link){ 2 //if(link==NULL){return;} 3 //没有节点或者只有一个节点时 不需要做任何事情 4 if(link->next == NULL || link->next->next == NULL){ 5 return; 6 } 7 SNode *prevNode = link->next;//第一个节点 前面节点 8 SNode *currNode = prevNode->next;//第二个节点 9 while(currNode != NULL){ 10 SNode *nextNode = currNode->next;//记录当前节点的后一个节点 11 currNode->next = prevNode;//让当前节点的next指向之前的前节点 12 prevNode = currNode;//前节点变 指向现在当前的节点 13 currNode = nextNode;//当前的节点 指向 下一个节点 14 } 15 link->next->next = NULL;//让原来的第一个节点的next指向NULL 16 link->next = prevNode;//让头节点next指向原来最后的那个节点 17 }
三、双向链表
1 typedef int T; 2 3 typedef struct DNode{ 4 T data; 5 struct DNode *prev; 6 struct DNode *next; 7 }DNode; 8 9 typedef struct DLink{ 10 //头尾结点不存储数据 11 DNode *head;//指向头节点 12 DNode *tail;//指向尾节点 13 }DLink;
1.初始化链表
申请头节点和尾节点的动态内存
双向链表的头节点prev指向NULL next指向尾节点 尾节点prev指向头节点 next指向NULL
1 void init(DLink *dlink){ 2 dlink->head = malloc(sizeof(DNode)); 3 dlink->tail = malloc(sizeof(DNode)); 4 dlink->head->prev = NULL; 5 dlink->head->next = dlink->tail; 6 dlink->tail->prev = dlink->head; 7 dlink->tail->next = NULL; 8 }
2.清空链表所有节点
1 void clear(DLink *dlink){ 2 DNode *node = dlink->head->next; 3 while(node != dlink->tail){ 4 DNode *next = node->next; 5 free(node); 6 node = next; 7 } 8 dlink->head->next = dlink->tail; 9 dlink->tail->prev = dlink->head; 10 }
3.销毁链表
1 oid destroy(DLink *dlink){ 2 clear(dlink); 3 free(dlink->head); 4 dlink->head = NULL; 5 free(dlink->tail); 6 dlink->tail = NULL; 7 }
4.链表是否为空
1 bool isEmpty(DLink dlink){ 2 return dlink.head->next == dlink.tail && dlink.head == dlink.tail->prev; 3 }
5.链表元素个数
1 size_t size(DLink dlink){ 2 DNode *node = dlink.head->next; 3 size_t cnt = 0; 4 while(node != dlink.tail){ 5 cnt++; 6 node = node->next; 7 } 8 return cnt; 9 }
6.从头部插入一个元素
需要创建新的节点,申请新的动态内存
头节点的prev还是指向NULL,next指向新节点
尾节点的next还是指向NULL,prev指向新节点
新节点的next指向尾节点,prev指向头节点
1 void insertFront(DLink dlink,T data){ 2 DNode *node = malloc(sizeof(DNode)); 3 node->data = data; 4 node->next = dlink.head->next; 5 node->prev = dlink.head; 6 dlink.head->next->prev = node; 7 dlink.head->next = node; 8 }
7.获得上一个节点
1 static DNode * getPrevNode(DLink dlink,size_t index){ 2 DNode *node = dlink.head; 3 for(int i=0;i<index;i++){ 4 node = node->next; 5 } 6 return node; 7 }
8.根据下标插入一个元素
1 void insert(DLink dlink,size_t index,T data){ 2 if(index > size(dlink)){ 3 return; 4 } 5 DNode *prevNode = getPrevNode(dlink,index); 6 DNode *node = malloc(sizeof(DNode)); 7 node->data = data; 8 node->next = prevNode->next; 9 node->prev = prevNode; 10 prevNode->next->prev = node; 11 prevNode->next = node; 12 13 }
9.根据下标删除一个元素
1 void deleteByIndex(DLink dlink,size_t index){ 2 if(index >= size(dlink)){ 3 return; 4 } 5 DNode *currNode = getPrevNode(dlink,index+1); 6 currNode->next->prev = currNode->prev; 7 currNode->prev->next = currNode->next; 8 free(currNode); 9 }
10.从头部开始遍历链表
1 void travelFront(DLink dlink){ 2 DNode *node = dlink.head->next; 3 while(node != dlink.tail){ 4 printf("%d ",node->data); 5 node = node->next; 6 } 7 printf(" "); 8 }
11.从尾部开始遍历链表
1 void travelBack(DLink dlink){ 2 DNode *node = dlink.tail->prev; 3 while(node != dlink.head){ 4 printf("%d ",node->data); 5 node = node->prev; 6 } 7 printf(" "); 8 }