双链表实现
双链表是在单链表的基础上增加一个指向前一个节点的指针。使节点持有它前后两个节点指针。从而实现逆序遍历。
双链表的数据操作与单链表相差不大,只比单链表多修改一个指针,在插入和删除操作时要对首节点和最后一个节点特叔处理。具体实现见代码
结构体分析
先来看一下结构体的定义,如下,person的结构里只有数据域和两个前后节点,是最精简的了,没有多余数据。
typedef struct person2{
char name[10];
int age;
struct person2 *next;
struct person2 *last;
}person2;
再来看linkedList结构体的定义。如下,这个结构体可以理解为是专门记录链表信息的管理型结构体,或者是专门操作person结构的工具。所以他持有链表头尾节点,表长。每条链表只含有有一个linkedList结构,或者说每一个linkedList代表一条链表。
这样linkedList里的数据就只有一份,空间复杂度O(1),已是最低了。
这里称linkedList里封装的head节点为头结点head,它的next指向链表第一个元素,称为首节点。所以头结点是不包含在链表上的,只是head节点的next指针指向首节点,end节点的last指针指向链表最后一个节点。
typedef struct linkedList{
struct person2 *head;
struct person2 *end;
struct person2 *before;//先序遍历用的指针
struct person2 *after;//后序遍历用的指针
int length;
}linkedList;
实现代码如下:
//新建person
person2 *newP2(char *name,int age){
//动态分配内存,初始化person
person2 *p = (person2 *)malloc(sizeof(person2));
p->age = age;
strcpy(p->name,name);
p->next = NULL;
p->last = NULL;
return p;
}
//建立空表
linkedList *newList(){
//建立头结点
linkedList *link = (linkedList *)malloc(sizeof(linkedList));
//为头尾指针分配实体
link->head = newP2("",-1);
link->end = newP2("",-1);
//使头尾互指 head->last和end->next 为null,
link->head->next = link->end;
link->end->last = link->head;
link->after = link->end;
link->before = link->head;
link->length = 0;
return link;
}
//add element in first
void addFirst(linkedList *link, person2 *p){
person2 *head = link->head->next;
//让新节点的next指向head->next
p->next = head;
//让head->next->last指向新元素
head->last = p;
//修改队首指针
link->head->next = p;
link->length++;
}
//add element in end
void addEnd(linkedList *link, person2 *p){
person2 *end = link->end->last;
//让新节点p的last指针指向最后一个元素end 让新元素p的last指向end->last;
p->last = end;
//让end 所指节点的next指针指向新节点p
end->next = p;
//修改队尾指针
link->end->last = p;
link->length++;
}
//根据index序号读表元素,返回数据域data(index序号按写入顺序,从0开始)
person2 *read(linkedList *link, int index){
if(link->length > index && index >= 0 ){
person2 *head = link->head->next;
int j = 0;
while (head != NULL && j < index){
head = head->next;
j++;
}
if(j == index){
return head;
}
}
return NULL;
}
//定位元素 返回下标(从0开始)
int locatep2(linkedList *link, person2 *p){
int i;
person2 *head = link->head->next;
for(i = 0; head != NULL; i++){
if(compareP2(head,p)){
return i;
}
head = head->next;
}
return -1;
}
//person 比较器
int compareP2(person2 *p1,person2 *p2){
if(p1 == p2){
return 1;
}
return (p1->age == p2->age && !strcmp(p1->name,p2->name));
}
//insert element before index
//实现思路:先找到index节点(调用read()),在修改指针。
void insertlink(linkedList *link,person2 *p,int index){
person2 *p1;
//得到要插入位置的元素
if(index == 0 ){
addFirst(link,p);
return;
}else if(index == link->length){
addEnd(link,p);
return;
}else{
p1 = read(link,index);
}
//insert element
if(p1 != NULL){
//新元素p的next指向index处元素p1
p->next = p1;
//新元素p的last指向index处元素p1的last
p->last = p1->last;
//修改插入位置两边的节点指向,使其指向新节点p(元素 == 节点,原谅我懒得改成统一)
p1->last = p;
p->last->next = p;
link->length++;
}else{
printf("index not find");
exit(1);
}
}
//delete element by index
//实现思路:先找到index节点(调用read()),再修改指针。
void myRemove2(linkedList *link,int index){
person2 *p1;
if(index == 0 ){
//指向第一个节点的head
person2 *head = link->head;
//待删除元素p1指向首节点
p1 = head->next;
//使head指向下下一个节点
head->next = head->next->next;
//使现在head的下一个元素的last指针为null(便于遍历元素)
head->next->last = NULL;
}
else if(index == link->length-1){
person2 *end = link->end;
p1 = end->last;
end->last = end->last->last;
end->last->next = NULL;
}
else{ //不是首尾节点的情况
//得到要插入位置的节点
p1 = read(link,index);
//delete element
if(p1 == NULL){
printf("index not find");
exit(1);
}
p1->last->next = p1->next;
p1->next->last = p1->last;
}
free(p1);//一定要释放节点空间
link->length--;
}
//先序遍历
person2 *before(linkedList *link){
person2 *p = link->before->next;
if(p != NULL){
link->before = p;
return p;
}
link->before = link->head;
return NULL;
}
//后序遍历
person2 *after(linkedList *link){
person2 *p = link->after->last;
if(p != NULL){
link->after = p;
return p;
}
link->after = link->end;
return NULL;
}
//print element
void linkPrint(linkedList *head){
int length = head->length;
person2 *begin = head->head->next;
printf("***********length = : %d ************\n",length);
for(;begin != NULL; begin = begin->next){
printf("name: %s,age: %d\n",begin->name,begin->age);
}
}
void doubleLinkMain(){
//linkedList *p1;
person2 *p2;
//初始化 link list
linkedList *head = newList();
addEnd(head,newP2("HH",0));
addEnd(head,newP2("HH",1));
addEnd(head,newP2("HH",2));
addEnd(head,newP2("HH",3));
addEnd(head,newP2("HH",4));
addEnd(head,newP2("HH",5));
linkPrint(head);
addFirst(head,newP2("ff",1));
addFirst(head,newP2("ff",2));
addFirst(head,newP2("ff",3));
addFirst(head,newP2("ff",4));
addFirst(head,newP2("ff",5));
linkPrint(head);
person2 *p1 = read(head,2);
if(p1 != NULL){
printf("read index 2 :%s++%d\n",p1->name,p1->age);
}
printf("found person(ff,2) index is %d\n",locatep2(head,newP2("ff",2)));
//test insert element
printf("insert element 0 ,4 \n");
insertlink(head,newP2("new",66),0);
insertlink(head,newP2("new",66),4);
linkPrint(head);
//test delete element
printf("delete element 0 ,6, 12\n");
myRemove2(head, 0);
myRemove2(head, 6);
myRemove2(head, 10);
linkPrint(head);
printf("\n\n");
while((p2 = before(head)) != NULL){
printf("先序遍历 :%s,%d\n",p2->name,p2->age);
}
while((p2 = after(head)) != NULL){
printf("后序遍历 :%s,%d\n",p2->name,p2->age);
}
}