单链表是一种先行表链接存储表示。链表是由N个节点组成的,每个节点可以包括两个部分:1、数据结构体 2、指向下一个节点的指针
typedef int DataType;
//node结构体
typedef struct node
{
DataType data;
struct node *Link;
}ListNode;
链表相对于数组的优势是,可以在内存中随机找个空间,可以是连续的,也可以是不连续,但是要保证空间能容的下一个节点的大小。
最要在链表的第一个给一个空头,好处是把这个空头的位置定义为0,有数据的第一个定义为1,这样方便查找。
还有图个好处插入的时候可以不用管是不是头节点直接用一种方式插入就好,要是没有头节点的话,就要考虑是不是头节点的问题了。
给出两个重要的操作 增加和删除
1、增加(加入要插入到第n个位置)
增加头节点的时候只要记住:四步:
a、malloc一个节点
b、找到n-1位置上的节点
c、把第n个节点和新节点连接起来
d、把第n个和第n-1个节点断开,同时把n-1个节点个新节点连上
int Insert(ListNode *first, int i, DataType x)
{
//备份指针
ListNode *temp = first;
//为新的节点分配空间
ListNode *newnode = (ListNode *)malloc(sizeof(ListNode));
//判断空间是否分配成功
if (!newnode)
{
printf("分配失败\n");
return 0;
}
//给新节点赋值
newnode->data = x;
newnode->Link = NULL;
//寻找要插入位置的前一个节点
for (int count = 0; count < i-1 ; count++)
{
temp = temp->Link;
}
//先把要插入节点位置原来的节点连接到新节点上,然后再把要插入节点的前一个节点和新节点连接起来
newnode->Link = temp->Link;
temp->Link = newnode;
return 1;
}
2、删除(加入要删除出第N个)
这分三步走:
a、找到第n-1个节点
b、把n-1和n+1节点连上
c、free掉节点n
void reMove(ListNode *first, int i, DataType x)
{
//备份指针
ListNode *temp = first,*pBefore,*pCurr;
//判断位置是否正确
if (i > getLength(first))
{
printf("位置错误\n");
return;
}
//寻找要删除位置的前一个节点
for (int count = 0; count < i - 1; count++)
{
temp = temp->Link;
}
//确定删除节点之前的节点
pBefore = temp;
//确认删除的节点
pCurr = temp->Link;
if (pCurr->data != x)
{
printf("请输入正确的数据\n");
return;
}
//把要删除节点的前一个和后一个连接上
pBefore->Link = pCurr->Link;
//释放要删除的节点
free(pCurr);
}
有几个需要注意的地方:
1)定义指针时而且要使用指针指向某个结构体或者字符串的时候,需要分配内存空间大小(这个很重要,经常有人会忘记,导致段错误或者内存错误访问的错误)
2)当指针位及时被使用时需要把指针指向NULL
这个还有一个要注意:NULL就是NULL不是0地址,也不是其他的什么地址,不要勿以为是0(判断指针时候要用 p==NULL或者p !=NULL,我上面有的写的不够规范,大家见谅)
下面给出全部代码
头文件:
#ifndef _HEAD_H_
#define _HEAD_H_
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define OK 1
#define ERROR 0
typedef int DataType;
//node结构体
typedef struct node
{
DataType data;
struct node *Link;
}ListNode;
//初始化函数
void initList(ListNode *first);
//计算链表长度
int getLength(ListNode *first);
//插入函数
int Insert(ListNode *first, int i, DataType x);
//输出内容
void outPut(ListNode *first);
//删除节点
void reMove(ListNode *first, int i, DataType x);
//查找结点
int Search(ListNode *first, DataType x);
void printMsg();
int getOption();
#endif
函数文件
#include "head.h"
void initList(ListNode *first)
{
//如果失败就返回
if (!first)
{
printf("分配空间失败\n");
return;
}
//把下一个节点置空,因为原则上不允许野指针,这很危险
first->Link = NULL;
}
int getLength(ListNode *first)
{
//备份指针,为了不影响后面的操作
ListNode *temp = first->Link;
int count = 0;
while (temp != NULL)
{
temp = temp->Link;
count++;
}
return count;
}
int Insert(ListNode *first, int i, DataType x)
{
//备份指针
ListNode *temp = first;
//为新的节点分配空间
ListNode *newnode = (ListNode *)malloc(sizeof(ListNode));
//判断空间是否分配成功
if (!newnode)
{
printf("分配失败\n");
return 0;
}
//给新节点赋值
newnode->data = x;
newnode->Link = NULL;
//寻找要插入位置的前一个节点
for (int count = 0; count < i-1 ; count++)
{
temp = temp->Link;
}
//先把要插入节点位置原来的节点连接到新节点上,然后再把要插入节点的前一个节点和新节点连接起来
newnode->Link = temp->Link;
temp->Link = newnode;
return 1;
}
void outPut(ListNode *first)
{
//备份指针
ListNode *temp = first;
//判断链表是否为空
if (!temp->Link)
{
printf("链表是空的\n");
return;
}
//循环输出内容
for (int i = 0; i < getLength(first); i++)
{
temp = temp->Link;
printf("%d ", temp->data);
}
printf("\n");
}
void reMove(ListNode *first, int i, DataType x)
{
//备份指针
ListNode *temp = first,*pBefore,*pCurr;
//判断位置是否正确
if (i > getLength(first))
{
printf("位置错误\n");
return;
}
//寻找要删除位置的前一个节点
for (int count = 0; count < i - 1; count++)
{
temp = temp->Link;
}
//确定删除节点之前的节点
pBefore = temp;
//确认删除的节点
pCurr = temp->Link;
if (pCurr->data != x)
{
printf("请输入正确的数据\n");
return;
}
//把要删除节点的前一个和后一个连接上
pBefore->Link = pCurr->Link;
//释放要删除的节点
free(pCurr);
}
int isEmpty(ListNode *first)
{
return (first->Link == NULL) ? 1 : 0;
}
int Search(ListNode *first, DataType x)
{
ListNode *temp = first;
int i = 0;
//判断链表是否为空
if (!temp->Link)
{
printf("链表是空的\n");
return -1;
}
while (temp->data != x)
{
i++;
temp = temp->Link;
}
return i;
}
void printMsg()
{
printf("1、插入一个数据\n");
printf("2、删除一个数据\n");
printf("3、获得一个元素位子\n");
printf("4、查看所有数据\n");
printf("5、查看表是否为空\n");
printf("6、退出\n");
printf(">> ");
}
int getOption()
{//获取用户输入操作
char input;
scanf("%c", &input);
_flushall();
//fflush(stdin);
//input=toupper(input);
return input;
}
main函数
#include "head.h"
void main(void)
{
int i,x,op;
ListNode first;
initList(&first);
printMsg();
while (op = getOption())
{
switch (op)
{
case '1':
printf("请输入位置和元素\n");
scanf("%d %d", &i, &x);
_flushall();
Insert(&first,i,x);
printf("插入元素%d", x);
break;
case '2':
printf("请输入位置和元素\n");
scanf("%d %d", &i, &x);
_flushall();
reMove(&first, i, x);
printf("删除元素%d\n", x);
break;
case '3':
printf("请输入要查找的元素\n");
scanf("%d", &x);
_flushall();
printf("元素在%d\n", Search(&first,x));
break;
case '4':
outPut(&first);
break;
case '5':
printf("%d", isEmpty(&first));
break;
case '6':
printf("---byebye----");
return;
break;
default:
break;
}
printf("\n>> ");
}
system("pause");
}
程序的封装和接口的概念很重要,要尽量使用接口的封装