線性表之單鏈表
單鏈表的設計之初,筆者在考慮一個首要的問題,就是單鏈表的節點是在插入的函數內部創建,還是在函數外部創建。考慮到用戶在插入的時候,變量生命週期的不確定性以及容易造成內存泄漏等問題,綜合考慮之下使用了內部創建節點的方式。筆者設計的單鏈表中包含了單鏈表的反轉和合並等有趣的操作,其中的奧妙如果讀者有興趣可以細究。比如爲什麼筆者使用了二級指針來合併單鏈表,而不直接使用一級指針,比如爲什麼在清空鏈表節點時,根據length清空會有可能會發生內存泄漏....等等。下面是代碼:
#ifndef LINKEDLIST_H
#define LINKEDLIST_H
#ifndef NULL
#define NULL 0
#endif
/* 元素類型 */
typedef int elem_t;
/* 節點結構體 */
typedef struct _tag_linked_list_node
{
elem_t data;
struct _tag_linked_list_node *next;
}ll_node;
/* 鏈表結構體 */
typedef struct _tag_linked_list
{
ll_node head; /* 啞結點 */
int length; /* 鏈表長度 */
}linked_list;
/**
* 創建鏈表
* @return 返回鏈表指針,NULL表示創建失敗
*/
linked_list *linked_list_create();
/**
* 從頭部插入元素
* @param plist 鏈表指針
* @param pe 被插入元素的指針
* @return 1:成功 0:失敗
*/
int linked_list_insert(linked_list *plist,elem_t *pe);
/**
* 從尾部插入元素
* @param plist 鏈表指針
* @param pe 被插入元素的指針
* @return 1:成功 0:失敗
*/
int linked_list_append(linked_list *plist,elem_t *pe);
/**
* 查找元素
* @param plist 鏈表指針
* @param i 元素位置索引
* @param pe 用於保存被查找元素的值的指針
* @return 1:成功 0:失敗
*/
int linked_list_get(linked_list *plist,int i,elem_t *pe);
/**
* 按索引刪除元素
* @param plist 鏈表指針
* @param i 被刪除元素的索引
* @param pe 用於保存被刪除元素的值的指針
* @return 1:成功 0:失敗
*/
int linked_list_remove(linked_list *plist,int i,elem_t *pe);
/**
* 鏈表反轉
* @param plist 鏈表指針
* @return 1:成功 0:失敗
*/
int linked_list_reverse(linked_list *plist);
/**
* 鏈表合併,將第二個鏈表鏈接到第一個鏈表,第二個鏈表指針置空
* @param dest 合併的目標鏈表
* @param pp_src 被合併的鏈表的指針的指針
* @return 返回合併後的鏈表的指針(第一個參數)
*/
linked_list *linked_list_merge(linked_list *dest,linked_list **pp_src);
/**
* 清空鏈表
* @param plist 鏈表指針
* @return 1:成功 0:失敗
*/
int linked_list_clear(linked_list *plist);
/**
* 銷燬鏈表
* @param plist 鏈表指針
* @return 1:成功 0:失敗
*/
int linked_list_destroy(linked_list *plist);
#endif // LINKEDLIST_H
#include "LinkedList.h"
#include <malloc.h>
/**
* 創建鏈表
* @return 返回鏈表指針,NULL表示創建失敗
*/
linked_list *linked_list_create()
{
linked_list *plist = (linked_list *)malloc(sizeof(linked_list));
if(plist != NULL)
{
plist->length = 0;
plist->head.next = NULL;
}
return plist;
}
/**
* 從頭部插入元素
* @param plist 鏈表指針
* @param pe 被插入元素的指針
* @return 1:成功 0:失敗
*/
int linked_list_insert(linked_list *plist,elem_t *pe)
{
int ret = ( (plist != NULL) && (pe != NULL));
if(ret)
{
ll_node *node = (ll_node *)malloc(sizeof(ll_node));
ll_node *head = &(plist->head);
if(node != NULL)
{
node->next = head->next;
head->next = node;
node->data = *pe;
plist->length++;
}
else
{
ret = 0;
}
}
return ret;
}
/**
* 從尾部插入元素
* @param plist 鏈表指針
* @param pe 被插入元素的指針
* @return 1:成功 0:失敗
*/
int linked_list_append(linked_list *plist,elem_t *pe)
{
int ret = ( (plist != NULL) && (pe != NULL));
if(ret)
{
ll_node *node = (ll_node *)malloc(sizeof(ll_node));
if(node != NULL)
{
int pos = 0;
ll_node *current = &(plist->head);
/* 移動到尾部 */
for(pos = 0;pos < plist->length;pos++)
{
current = current->next;
}
node->next = NULL;
node->data = *pe;
current->next = node;
plist->length++;
}
else
{
ret = 0;
}
}
return ret;
}
/**
* 查找元素
* @param plist 鏈表指針
* @param i 元素位置索引
* @param pe 用於保存被查找元素的值的指針
* @return 1:成功 0:失敗
*/
int linked_list_get(linked_list *plist,int i,elem_t *pe)
{
int ret = ( (plist != NULL) && (pe != NULL) && (i >= 0) && (i < plist->length));
if(ret)
{
int pos = 0;
ll_node *current = plist->head.next;
/* 移動到尾部 */
for(pos = 0;pos < i;pos++)
{
current = current->next;
}
*pe = current->data;
}
return ret;
}
/**
* 按索引刪除元素
* @param plist 鏈表指針
* @param i 被刪除元素的索引
* @param pe 用於保存被刪除元素的值的指針
* @return 1:成功 0:失敗
*/
int linked_list_remove(linked_list *plist,int i,elem_t *pe)
{
int ret = ( (plist != NULL) && (pe != NULL) && (i >= 0) && (i < plist->length));
if(ret)
{
int pos = 0;
ll_node *pre = &(plist->head);
for(pos = 0;pos < i;pos++)
{
pre = pre->next;
}
/* 保存要被刪除的元素的值 */
*pe = pre->next->data;
pre->next = pre->next->next;
plist->length--;
}
return ret;
}
/**
* 鏈表反轉
* @param plist 鏈表指針
* @return 1:成功 0:失敗
*/
int linked_list_reverse(linked_list *plist)
{
int ret = (plist != NULL);
if(ret)
{
if(plist->length > 1)
{
int i;
/* 保存尾元素的指針 */
ll_node *tail = &(plist->head);
for(i = 0;i < plist->length;i++)
{
tail = tail->next;
}
/* 將頭元素的位置調整到原尾元素的後部插入 */
while(plist->head.next != tail)
{
/* 保存頭部元素的指針 */
ll_node *old_head = plist->head.next;
/* 調整頭結點指針的位置,指向原頭部的下一個元素 */
plist->head.next = old_head->next;
/* 將保存的原頭部元素插入到原尾部元素的後面 */
old_head->next = tail->next;
tail->next = old_head;
}
}
}
return ret;
}
/**
* 鏈表合併,將第二個鏈表鏈接到第一個鏈表,第二個鏈表指針置空
* @param dest 合併的目標鏈表
* @param pp_src 被合併的鏈表的指針的指針
* @return 返回合併後的鏈表的指針(第一個參數)
*/
linked_list *linked_list_merge(linked_list *dest,linked_list **pp_src)
{
if((dest != NULL) && ( (*pp_src) != NULL) && ((*pp_src)->length != 0))
{
int i;
ll_node *current = &(dest->head);
/* 移動到目標鏈表的尾部元素 */
for(i = 0;i < dest->length;i++)
{
current = current->next;
}
current->next = (*pp_src)->head.next;
dest->length += (*pp_src)->length;
free(*pp_src);
*pp_src = NULL;
}
return dest;
}
/**
* 清空鏈表
* @param plist 鏈表指針
* @return 1:成功 0:失敗
*/
int linked_list_clear(linked_list *plist)
{
int ret = ((plist != NULL) && (plist->length != 0));
if(ret)
{
/*
* 這種清空的循環不妥,當外部惡意修改了plist->length,那麼將會發生內存泄漏
int i;
for(i = 0;i < plist->length;i++)
{
ll_node *del = plist->head.next;
plist->head.next = del->next;
free(del);
}
*/
ll_node *head = &(plist->head);
while(head->next != NULL)
{
ll_node *del = head->next;
head->next = del->next;
free(del);
}
plist->length = 0;
}
return ret;
}
/**
* 銷燬鏈表
* @param plist 鏈表指針
* @return 1:成功 0:失敗
*/
int linked_list_destroy(linked_list *plist)
{
int ret = (plist != NULL);
if(ret)
{
linked_list_clear(plist);
free(plist);
}
return ret;
}