数据结构——c单链表实现
感觉使用头结点的链表不好看,为了让链表结构更好看点,所以就使用指针,让链表指针直接指向链表第一个结点。
在熟悉链表的实现后,栈和队列就更容易实现了,例如
设计链表有如下API
1. void insertToHead(List *l, int val); //在链表头部插入一个结点
2.void insertToTail(List *l, int val); //在链表的尾部插入一个结点
3.int deleteHeadElem(List *l); //删除链表头部的结点
4.void deleteTailElem(List *l); //删除链表尾部的结点
栈的实现:后进先出, 栈指针指向栈顶结点,栈的操作调用链表API( 1, 3)
队列的实现: 先进先出, 队列结点指向链表头和链表尾,队列的操作调用链表API(1, 4)
单链表实现如下:
#include<stdlib.h> typedef struct _list{ int data; struct _list *next; } LNode, *List; void initList(List *l) { *l = NULL; } // 在头部插入 void insertToHead(List *l, int val) { List temp = (List) malloc(sizeof(LNode)); if(*l == NULL) { temp->data = val; temp->next = NULL; *l = temp; return; } temp->data = val; temp->next = *l; *l = temp; } // 在尾部插入 void insertToTail(List *l, int val) { List temp = (List)malloc(sizeof(LNode)); if(*l == NULL) { temp->data = val; temp->next = NULL; *l = temp; //在原指针地址空间写入值 return; } List t = *l; while(t->next != NULL) { t = t->next; } temp->data = val; temp->next = NULL; t->next = temp; } // 获取链表的长度 int getLength(List l) { if(l == NULL) return 0; int i = 0; while(l) { i++; l = l->next; } return i; } // 返回链表的第K个元素 int getPositionK(List l, int k) { int len = getLength(l); if(k < 0 || k > len) { printf("位置 %d太大,超出链表长度\n",k); return -1; } // l直接指向元素,k 为 2 , l->data第一个 l->next->data第二个 k--; while(k != 0 ) { l = l->next; k--; } return l->data; } // 删除头部元素 void deleteHeadElem(List *l) { if(*l == NULL) return; List t = *l; // t指向第二个结点 t = t->next; // 释放第一个结点 printf("销毁结点值为:%d\n", (*l)->data); free(*l); *l = t; } // 删除尾部的元素 void deleteTailElem(List *l) { if(*l == NULL) return; List t = *l; // t 指向倒数第二个元素 while(t->next->next) t = t->next; printf("删除尾部的元素值为:%d\n", t->next->data); free(t->next); t->next = NULL; //必须置为NULL,否则出错 } // 按值查找是否存在 // 0 不存在 // 1 存在 int findValExist(List l, int val) { if(l == NULL) { printf("链表为空\n"); return -1; } while(l) { if(l->data == val) return 1; l = l->next; } return 0; } // 链表反转 void reverseList(List *l) { if(*l == NULL) return; List p, q, t; p = *l; // p 指向链表的第一个结点 q = p->next; // q指向链表的第二个结点 p->next = NULL; //第一个结点指针域设置为NULL while(q && q->next) { t = q->next; q->next = p; p = q; q = t; } // q指向最后一个元素时停止循环, p指向倒数第二个元素 q->next = p; *l = q; } // 删除链表的倒数第K个结点,必须要改变 指针i里面的值,因为有可能删除头结点 void deletePositionK(List *l, int k) { if(*l == NULL) return; int len = getLength(*l); if( k<0 || k > len) { printf("k值错误\n"); return; } List p = *l; if(len == k) //删除首结点 { *l = p->next; free(p); return; } while(len - k > 1) { p = p->next; k++; } // p指向倒数第 k+1个结点,也就是被删除结点的前一个结点 List t = p->next; p->next = t->next; free(t); } void printList(List l) { while(l) { printf("%2d",l->data); l = l->next; } printf("\n"); }
运行如下:
$ ./list 7 6 5 3 4 链表长度为:5, 第二个元素为:6 消耗结点值为:7 删除尾部的元素值为:4 元素3存在链表中 6 5 3 反转链表: 3 5 6 删除倒数第3个元素 5 6
在各个函数中,有的传一级指针有的传二级指针,需要看情况考虑
1. 如果在函数中不改变链表的结构,则使用一级指针
2. 如果在函数中改变链表的结构,则使用二级指针
举个例子,初始化的时候
在main(
list l; // l是指针
initList(&l); // &l 是二级指针
)
在initList(list *t)
{
*t = NULL;
}
我们在main中传递 &l 也就是l的地址,然后函数调用时 *t = NULL 就可以把main函数中的l设置为NULL
一干解千愁

浙公网安备 33010602011771号