单向链表
一、定义
将存储数据元素信息的域称为数据域,将存储直接后继位置的域称为指针域。指针域中存储的信息称做指针或链。这两部分信息组成数据元素称为结点。
二、头指针和头节点异同
1、头指针是指向链表第一个结点的指针。注,并不一定是头结点的指针。
无论链表是否为空,头指针均不为空。
2、头结点,为了操作的统一和方便而设立的,放在第一个元素结点之前,其数据域一般无意义。有了头结点,对在第一元素结点前插入和删除第一个结点,其操作与其他结点操作就统一了。
三、代码 (不带头结点的写法)
#ifndef __LINKEDLIST_H
#define __LINKEDLIST_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define OK 1
#define ERROR 0
typedef int states;
/** 结点数据 */
typedef struct {
int id;
char name[30];
}ElementType;
/** 线性表的单链表存储结构 */
typedef struct linkedNode{
ElementType data; //数据域
struct linkedNode *next; //指针域
}LinkedNode;
/** 线性表的单链表表头结构 */
typedef struct linkedList{
struct linkedNode *headptr;
int length;
}LinkedList;
/** 链表初始化 */
states InitLinkedList(LinkedList *ptrl);
/** 向链表中插入结点 (按照位置下标插入)*/
states InsertLinkedList(LinkedList *ptrl, int pos, ElementType data);
/** 从链表中删除结点 */
states DeleteLinkedList(LinkedList *ptrl, int pos, ElementType *data);
/** 单链表的读取 */
states GetLinkedListElem(LinkedList *ptrl, int pos, ElementType *data);
/** 获取链表中结点个数 */
int GetLinkedListLength(LinkedList *ptrl);
/** 链表的整表创建操作 --头插法 */
void CreateLinkedList_H(LinkedList *ptrl);
/** 链表的整表创建操作 --尾插法 */
void CreateLinkedList_T(LinkedList *ptrl);
/** 链表的整表删除操作 */
states ClearLinkedList(LinkedList *ptrl);
/** 链表节点的打印 */
void PrintfLinkedList(LinkedList *ptrl);
#endif /* __LINKEDLIST_H */
#include "linkedlist.h"
/** 链表初始化 */
states InitLinkedList(LinkedList *ptrl)
{
ptrl->headptr = NULL;
ptrl->length = 0;
return OK;
}
/** 向链表中插入结点 (按照位置下标插入)*/
states InsertLinkedList(LinkedList *ptrl, int pos, ElementType data)
{
int i;
LinkedNode * tempNode, *node;
//判断pos的输入范围是否正确
if(pos < 1 || pos > ptrl->length + 1)
return ERROR;
//新建链表结点,
node = (LinkedNode *)malloc(sizeof(LinkedNode));
node->data = data;
node->next = NULL;
//分两种情况讨论:
if(pos == 1){
node->next = ptrl->headptr;
ptrl->headptr = node;
ptrl->length++;
return OK;
}
//寻找位置为 pos 的结点指针
tempNode = ptrl->headptr;
for(i = 1;i < pos - 1;i++){
tempNode = tempNode->next;
}
node->next = tempNode->next;
tempNode->next = node;
ptrl->length++;
return OK;
}
/** 从链表中删除结点 */
states DeleteLinkedList(LinkedList *ptrl, int pos, ElementType *data)
{
int i;
LinkedNode *tempnode,*node;
//判断链表是否为空
if(ptrl->headptr == NULL || ptrl->length == 0)
return ERROR;
//判断pos的输入范围是否正确
if(pos < 1 || pos > ptrl->length)
return ERROR;
if(pos == 1){
node = ptrl->headptr;
*data = node->data;
ptrl->headptr = node->next;
free(node);
ptrl->length--;
return OK;
}
tempnode = ptrl->headptr;
for(i = 1;i < pos - 1;i++){
tempnode = tempnode->next;
}
node = tempnode->next;
tempnode->next = node->next; //链接单链表
*data = node->data;
free(node);
ptrl->length--;
return OK;
}
/** 单链表的读取 */
states GetLinkedListElem(LinkedList *ptrl, int pos, ElementType *data)
{
int i;
LinkedNode *tempnode,*node;
//判断pos的输入范围是否正确
if(pos < 1 || pos > ptrl->length)
return ERROR;
if(pos == 1){
node = ptrl->headptr;
*data = node->data;
ptrl->headptr = node->next;
return OK;
}
tempnode = ptrl->headptr;
for(i = 1;i < pos - 1;i++){
tempnode = tempnode->next;
}
node = tempnode->next;
*data = node->data;
return OK;
}
/** 获取链表中结点个数 */
int GetLinkedListLength(LinkedList *ptrl)
{
return ptrl->length;
}
/** 链表的整表创建操作 --头插法 */
void CreateLinkedList_H(LinkedList *ptrl)
{
int id;
char name[30];
LinkedNode *node;
printf("请输入id:");
scanf("%d",&id);
if(id == -1)
return;
printf("请输入姓名:");
scanf("%s",name);
node = (LinkedNode *)malloc(sizeof(LinkedNode));
node->next = NULL;
node->data.id = id;
strcpy(node->data.name,name);
node->next = ptrl->headptr;
ptrl->headptr = node;
ptrl->length++;
CreateLinkedList_H(ptrl);
}
/** 链表的整表创建操作 --尾插法 */
void CreateLinkedList_T(LinkedList *ptrl)
{
int id;
char name[30];
static LinkedNode *tailNode;
LinkedNode *node;
while(1){
printf("请输入id:");
scanf("%d",&id);
if(id == -1)
break;
printf("请输入姓名:");
scanf("%s",name);
node = (LinkedNode *)malloc(sizeof(LinkedNode));
node->next = NULL;
node->data.id = id;
strcpy(node->data.name,name);
if(ptrl->length == 0){
ptrl->headptr = node;
tailNode = node;
ptrl->length++;
}else{
tailNode->next = node;
tailNode = node;
ptrl->length++;
}
}
}
/** 链表的整表删除操作 */
states ClearLinkedList(LinkedList *ptrl)
{
LinkedNode *q,*p;
p = ptrl->headptr;
while(p){
q = p->next;
free(p);
p = q;
ptrl->length--;
}
ptrl->headptr = NULL;
return OK;
}
/** 链表节点的打印 */
void PrintfLinkedList(LinkedList *ptrl)
{
int i;
LinkedNode *node = ptrl->headptr;
for(i = 0;i < ptrl->length;i++){
printf("[%d %s] ",node->data.id,node->data.name);
node = node->next;
}
}
四、编程笔记
-
向链表中插入结点 (按照位置下标插入)
states InsertLinkedList(LinkedList *ptrl, int pos, ElementType data);
1、判断输入的pos位置变量范围是否正确;
pos的范围为(1 <= pos <= ptrl->length + 1)
条件语句:if( pos < 1 || pos > ptrl->length + 1)
2、建立将要插入链表的的结点,将并其数据域赋值。
3、分两种情况进行讨论:
pos == 1时,需要改变链表中的头指针。
pos > 1时,不需要改变链表中的头指针。首先找到pos - 1位置的结点指针,将新建结点的指针域指向 pos-1结点的下个结点地址。再将pos - 1结点的指针域指向新建结点的地址。
寻找位于pos位置的结点,如下图:
当指针从结点1到结点2,指针需要移动1次;当指针从结点1到结点3,指针需要移动2次;所以判断循环条件为for(i = 1;i < pos;i++) { node = node->next;【结点1】-->【结点2】-->【结点3】-->NULL ↑ 【结点1】-->【结点2】-->【结点3】-->NULL ↑ 【结点1】-->【结点2】-->【结点3】-->NULL ↑ 【结点1】-->【结点2】-->【结点3】-->NULL ↑
-
从链表中删除结点(按照位置下标删除)
从链表删除元素与插入元素的操作相类似。关键在于要删除元素的获取。
1、判断链表是否为空。
2、判断pos的输入范围是否正确。(1 - ptrl->length)
3、分两种情况讨论;pos == 1时;pos >= 1时; -
创建单链表(头插法)
1、新建结点。
2、将新建结点的next指向ptrl->headptr;
3、然后将ptrl->headptr指向新建结点地址。