1、链式存储,可以简称链表。
2、链表的一个结点(node)由两部分组成:数据域和指针域。
3、整个链表的存取必须从头指针开始,链表最后一个结点的指针为空,因此它是非随机存取的数据结构。
4、链表中插入结点:假设原结点为p,新结点为s,则:
s->next = p->next;
p->next = s;
不是很难理解吧……
5、基本操作还是比较简单的,下面假定采用无“空头”模式的如下链表:
#define INVALID 0xfffffff
struct Node
{
int data;
struct Node* next;
};
void make_list(struct Node* head, int* arr, int n)
{
int i = 0;
struct Node* ptr = NULL;
if(n<=0)
{
return ;
}
// First node
head->data = arr[0];
head->next = NULL;
ptr = head;
for(i=1; i<n; i++)
{
ptr->next = (void*)malloc(sizeof(struct Node));
ptr = ptr->next;
if(!ptr)
{
return ;
}
ptr->data = arr[i];
}
ptr->next = NULL;
}
void free_list(struct Node* head)
{
struct Node* ptr = head->next;
struct Node* ptr_next = NULL;
while(ptr!=NULL)
{
ptr_next = ptr->next;
free(ptr);
ptr = ptr_next;
}
head->data = 0;
head->next = NULL;
}
void print_list(struct Node* head)
{
struct Node* ptr = head;
while(ptr!=NULL)
{
if(ptr->data!=INVALID)
{
printf("%d ", ptr->data);
ptr = ptr->next;
}
}
printf("\n");
}
6、假设链表A、B分别代表两个集合,现在要求A和B的并集操作。
基本思路:由于无法确定A、B都是有序的。检查B中每一个元素,如果在A中没有出现过,则插入到A中(注意集合的操作,必须考虑去重)。
void merge_list(struct Node* a, struct Node* b)
{
struct Node *pa, *pb;
pa = a;
pb = b;
// Check each node in b, if not found in a, add to a
while(pb!=NULL)
{
pa = a;
while(pa!=NULL)
{
if(pa->data == pb->data)
{
// Already in a, no need to insert
break;
}
pa = pa->next;
}
if(pa==NULL)
{
// pb can't found in a, insert
pa = a->next;
a->next = (void*)malloc(sizeof(struct Node));
a->next->data = pb->data;
a->next->next = pa;
}
pb = pb->next;
}
}
测试驱动如下:
int main()
{
struct Node head1, head2;
int arr1[5] = {1, 3, 5, 7, 9};
int arr2[5] = {8, 5, 4, 3, 2};
// Make list
make_list(&head1, &arr1[0], 5);
make_list(&head2, &arr2[0], 5);
// Print list
print_list(&head1);
print_list(&head2);
// Merge list
merge_list(&head1, &head2);
print_list(&head1);
// Free list
free_list(&head1);
free_list(&head2);
return 0;
}
输入:A = {1, 3, 5, 7, 9} B = {8, 5, 4, 3, 2}
输出:A={1,2, 4, 8, 3, 5, 7, 9}
7、还是A、B是集合,但都有序(如都递增),求A和B的并集操作。
基本思路:类似归并。
需要注意的细节是,由于采用无头结构,会多分配一个(预分配结点),需要在最后释放掉。
void merge_list2(struct Node* pa, struct Node* pb, struct Node* pc)
{
struct Node* pc_old = NULL;
pc->next = NULL;
pc->data = INVALID;
while(pa!=NULL && pb!=NULL)
{
if(pa->data < pb->data)
{
pc->data = pa->data;
pc->next = (void*)malloc(sizeof(struct Node));
pc_old = pc;
pc = pc->next;
pa = pa->next;
} else if(pa->data > pb->data)
{
pc->data = pb->data;
pc->next = (void*)malloc(sizeof(struct Node));
pc_old = pc;
pc = pc->next;
pb = pb->next;
} else
{
pc->data = pb->data;
pc->next = (void*)malloc(sizeof(struct Node));
pc_old = pc;
pc = pc->next;
pa = pa->next;
pb = pb->next;
}
}
while(pa!=NULL)
{
pc->data = pa->data;
pc->next = (void*)malloc(sizeof(struct Node));
pc_old = pc;
pc = pc->next;
pa = pa->next;
}
while(pb!=NULL)
{
pc->data = pb->data;
pc->next = (void*)malloc(sizeof(struct Node));
pc_old = pc;
pc = pc->next;
pb = pb->next;
}
if(pc_old)
{
free(pc_old->next);
pc_old->next = NULL;
}
}
测试驱动:
输入: A = {1, 3, 5, 7, 9} B = {1, 5, 9, 20, 27}
输出: C={1, 3, 5, 7, 9, 20, 27}
8、还是A、B是集合,还是都有序(如都递增),求A和B集合的交操作。
其实比并简单些:
void intersection_list2(struct Node* pa, struct Node* pb, struct Node* pc)
{
struct Node* pc_old = NULL;
pc->data = INVALID;
pc->next = NULL;
while(pa!=NULL && pb!=NULL)
{
if(pa->data==pb->data)
{
pc->data = pb->data;
pc->next = (void*)malloc(sizeof(struct Node));
pc_old = pc;
pc = pc->next;
pa = pa->next;
pb = pb->next;
continue;
}
else if(pa->data < pb->data)
{
pa = pa->next;
}
else//(pa->data > pb->data)
{
pb = pb->next;
}
}
if(pc_old)
{
free(pc_old->next);
pc_old->next = NULL;
}
}
8、也可以用数组来描述线性链表,土名静态链表。
typedef struct
{
int data;
int next;
}Node, StaticList[MAXSIZE];
在如上的数据结构中,data还是存储数据,而指针next被替换成了数组下标next。
由于空间是以数组的形式预分配的,不再有NULL来判断一个结点是否有效。
我们要牺牲首尾元素[0]和[MAXSIZE-1]来构成两条链:
(1)[MAXSIZE-1]开始的链,为有效链,包含的data是有效的,通过next依次接下去。
(2)从[0]开始的链,为未分配链,当发生delete的时候,需要把无效的结点挂在这条链上。
还是由于空间都是以结点的形式预分配的,malloc、free的参数、返回值都将是下标而不再是指针,这些也都需要我们自己实现了。
注意:free和malloc都是针对free链即[0]有关的操作。
liheyuan@liheyuan-desktop:tmp$ cat ./staticlist.c
#include <stdio.h>
#define MAXSIZE 100
typedef struct
{
int data;
int next;
}Node, StaticList[MAXSIZE];
// StaticList 's [0] and [MAXSIZE-1] is reserve, not for data storage
// StaticList[0] is free node list
// StaticList[MAXSIZE-1] is active node list
void sl_init(StaticList list)
{
// chunk the unused node(max-1 should reserve for data link)
int i = 0;
for(i=0;i<MAXSIZE-2;i++)
{
list[i].next = i+1;
}
// max-1 is active link, next==0 means end
list[MAXSIZE-1].next = list[MAXSIZE-2].next = 0;
}
int sl_malloc(StaticList list)
{
int i = list[0].next;
if(i==0)
{
return -1;
}
else
{
list[0].next = list[i].next;
return i;
}
}
void sl_free(StaticList list, int j)
{
list[i].next = list[0].next;
list[0].next = j;
}
int main()
{
int i;
StaticList list;
sl_init(list);
while((i = sl_malloc(list))!=-1)
{
printf("%d ", i);
}
return 0;
}
9、静态链表的插入、删除操作。
首先是简单的push_back和push_front:
//push_back
void sl_push_back(StaticList list, int val)
{
// Find the last elem in array
int i = MAXSIZE-1;
int new_pos = 0;
while(list[i].next!=0)
{
i = list[i].next;
}
// Append the elem
new_pos = sl_malloc(list);
list[i].next = new_pos;
list[new_pos].data = val;
list[new_pos].next = 0;
}
//push_front
void sl_push_front(StaticList list, int val)
{
// Append to the list head
int new_pos = sl_malloc(list);
list[new_pos].data = val;
list[new_pos].next = list[MAXSIZE-1].next;
list[MAXSIZE-1].next = new_pos;
}
然后是在指定位置删除和插入。
void sl_insert(StaticList list, int pos, int val)
{
// Find the right pos
int j = MAXSIZE - 1;
int i;
int new_pos;
if(pos<0)
{
return ;
}
for(i=0; i<pos && j!=0; i++)
{
j = list[j].next;
}
if(i!=pos)
{
return ;
}
// Insert
new_pos = sl_malloc(list);
if(new_pos==-1)
{
return ;
}
list[new_pos].next = list[j].next;
list[new_pos].data = val;
list[j].next = new_pos;
}
void sl_delete(StaticList list, int pos)
{
// Find the pos-1
int j = MAXSIZE - 1;
int i;
int free_pos;
if(pos<0)
{
return ;
}
for(i=0; i<pos-1 && j!=0; i++)
{
j = list[j].next;
}
if(i!=pos-1)
{
return ;
}
// Delete
free_pos = list[j].next;
list[j].next = list[free_pos].next;
sl_free(list, free_pos);
}
10、