单链表的简单操作与演示
单链表
单链表概念和简单的设计
单链表是一种链式存取的数据结构,链表中的数据是以结点来表示的,每个结点由元素和指针构成。
元素表示数据元素的映象,就是存储数据的存储单元;指针指示出后继元素存储位置,就是连接每个结点的地址数据。
以结点的序列表示的线性表称作线性链表,也就是单链表,单链表是链式存取的结构。
对于链表的每一个结点,我们使用结构体进行设计,其主要内容有:
其中,DATA数据元素,可以为你想要储存的任何数据格式,可以是数组,可以是int,甚至可以是结构体(这就是传说中的结构体套结构体)
NEXT为一个指针,其代表了一个可以指向的区域,通常是用来指向下一个结点,链表的尾部NEXT指向NULL(空),因为尾部没有任何可以指向的空间了
故,对于一个单链表的结点定义,可以代码描述成:
//定义结点类型typedefstructNode {int data; //数据类型,你可以把int型的data换成任意数据类型,包括结构体struct等复合类型structNode *next;//单链表的指针域} Node,*LinkedList; //Node表示结点的类型,LinkedList表示指向Node结点类型的指针类型
链表的初始化
初始化主要完成以下工作:创建一个单链表的前置节点并向后逐步添加节点,一般指的是申请结点的空间,同时对一个结点赋空值(NULL),其代码可以表示为:
LinkedList listinit(){Node L; L=(Node)malloc(sizeof(Node)); //开辟空间 if(L==NULL){ //判断是否开辟空间失败,这一步很有必要printf(“申请空间失败”);//exit(0); //开辟空间失败可以考虑直接结束程序 } L->next=NULL; //指针指向空}
注意:一定要判断是否开辟空间失败,否则生产中由于未知的情况造成空间开辟失败,仍然在继续执行代码,后果将不堪设想啦,因此养成这样的判断是很有必要的。
头插入法创建单链表
利用指针指向下一个结点元素的方式进行逐个创建,使用头插入法最终得到的结果是逆序的。如图所示:
从一个空表开始,生成新结点,并将读取到的数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头,即头结点之后。
//头插法建立单链表LinkedList LinkedListCreatH(){Node *L; L = (Node *)malloc(sizeof(Node)); //申请头结点空间 L->next = NULL; //初始化一个空链表int x; //x为链表数据域中的数据while(scanf(“%d”,&x) != EOF) { Node *p; p = (Node *)malloc(sizeof(Node)); //申请新的结点 p->data = x; //结点数据域赋值 p->next = L->next; /将结点插入到表头L–>|2|–>|1|–>NULL L->next = p; }return L;}
插入法创建单链表
如图所示为尾插入法的创建过程。
头插法生成的链表中,结点的次序和输入数据的顺序不一致。若希望两者次序一致,则需要尾插法。
该方法是将新结点逐个插入到当前链表的表尾上,为此必须增加一个尾指针r, 使其始终指向当前链表的尾结点,代码如下:
//尾插法建立单链表LinkedList LinkedListCreatT(){Node *L; L = (Node *)malloc(sizeof(Node)); //申请头结点空间 L->next = NULL; //初始化一个空链表 Node *r; r = L; //r始终指向终端结点,开始时指向头结点int x; //x为链表数据域中的数据while(scanf(“%d”,&x) != EOF) { Node *p; p = (Node *)malloc(sizeof(Node)); //申请新的结点 p->data = x; //结点数据域赋值 r->next = p; //将结点插入到表头L–>|1|–>|2|–>NULL r = p; } r->next = NULL;return L;}
遍历单链表如打印、修改
从链表的头开始,逐步向后进行每一个元素的访问,称为遍历。
对于遍历操作,我们可以衍生出很多常用的数据操作,比如查询元素,修改元素,获取元素个数,打印整个链表数据等等。
进行遍历的思路极其简单,只需要建立一个指向链表L的结点,然后沿着链表L逐个向后搜索即可,代码如下:
//便利输出单链表void printList(LinkedList L){Node *p=L->next;int i=0;while§{printf(“第%d个元素的值为:%d\n”,++i,p->data); p=p->next; }}
对于元素修改操作,以下是代码实现:
//链表内容的修改,在链表中修改值为x的元素变为为k。LinkedList LinkedListReplace(LinkedList L,int x,int k){Node *p=L->next;int i=0;while§{if(p->data==x){ p->data=k; } p=p->next; }return L;}
简单的遍历设计的函数只需要void无参即可,而当涉及到元素操作时,可以设计一个LinkedList类型的函数,使其返回一个操作后的新链表。
插入操作
链表的插入操作主要分为查找到第i个位置,将该位置的next指针修改为指向我们新插入的结点,而新插入的结点next指针指向我们i+1个位置的结点。
其操作方式可以设置一个前驱结点,利用循环找到第i个位置,再进行插入。
如图,在DATA1和DATA2数据结点之中插入一个NEW_DATA数据结点:
从原来的链表状态:
到新的链表状态:
代码实现如下:
//单链表的插入,在链表的第i个位置插入x的元素LinkedList LinkedListInsert(LinkedList L,int i,int x){Node *pre; //pre为前驱结点 pre = L;int tempi = 0;for (tempi = 1; tempi < i; tempi++) { pre = pre->next; //查找第i个位置的前驱结点 } Node *p; //插入的结点为p p = (Node *)malloc(sizeof(Node)); p->data = x; p->next = pre->next; pre->next = p;return L;}
删除操作
删除元素要建立一个前驱结点和一个当前结点,当找到了我们需要删除的数据时,直接使用前驱结点跳过要删除的结点指向要删除结点的后一个结点,再将原有的结点通过free函数释放掉。如图所示:
代码如下:
//单链表的删除,在链表中删除值为x的元素LinkedList LinkedListDelete(LinkedList L,int x) {Node *p,*pre; //pre为前驱结点,p为查找的结点。 p = L->next;while(p->data != x) { //查找值为x的元素 pre = p; p = p->next; } pre->next = p->next; //删除操作,将其前驱next指向其后继。 free§;return L;}
基本操作代码
package com.ma.linked;
public class DLLinkedList {
//定义一个头节点
private GoodsNode head = new GoodsNode(0,"",0);
//往链表中添加结点
public void add(GoodsNode node){
GoodsNode temp = head;
while (true){
if(temp.next == null){
break;
}
temp = temp.next;
}
temp.next = node;
}
//根据商品的id值进行添加,从小到大依次排序
public void addOrder(GoodsNode node){
GoodsNode temp = head;
boolean flag = false;
while (true){
if(temp.next == null){
break;
}
if (temp.next.id >node.id ){
break;
}else if (temp.next.id == node.id){
flag = true;
break;
}
temp = temp.next;
}
if (flag){
System.out.println("已经存在了此商品,请勿重复添加");
}else {
node.next = temp.next;
temp.next = node;
}
}
/*
* 修改结点
* 1.先找到链表中目标结点
* 2.根据新数据进行修改
* 3.根据商品编号进行查找
* */
public void updateNode(GoodsNode node){
//如果链表为空
if (head.next == null){
System.out.println("链表为空");
return;
}
GoodsNode temp = head.next;
boolean flag = false;
while (true){
if (temp == null){
break;
}
if (temp.id == node.id){
flag = true;
break;
}
temp = temp.next;
}
if (flag){
//真正的修改结点
temp.name = node.name;
temp.price = node.price;
}else {
System.out.println("你要修改的结点在链表中未找到");
}
}
//结点删除功能
public void delNode(int id){
GoodsNode temp = head;
boolean flag = false;
while (true){
if (temp.next == null){
break;
}
if (temp.next.id == id){
flag = true;
break;
}
temp = temp.next;
}
if (flag){
temp.next = temp.next.next;
}else {
System.out.println("没有找到要删除的结点" );
}
}
//查看链表中每一个结点元素
public void list(){
if (head.next == null){
return;
}
GoodsNode temp = head.next;
while (true){
if (temp == null){
break;
}
System.out.println(temp);
temp = temp.next;
}
}}
package com.ma.linked;
public class GoodsNode {
public int id;
public String name;
public double price;
public GoodsNode next;
public GoodsNode(int id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "GoodsNode{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
'}';
}
}
演示代码
package com.ma.linked;
public class LinkedTest {
public static void main(String[] args) {
GoodsNode goodsNode1 = new GoodsNode(1,"耐克鞋子",999.9);
GoodsNode goodsNode2 = new GoodsNode(2,"阿迪鞋子",777.9);
GoodsNode goodsNode3 = new GoodsNode(3,"李宁鞋子",555.9);
GoodsNode goodsNode4 = new GoodsNode(4,"安踏鞋子",222.9);
DLLinkedList dlLinkedList = new DLLinkedList();
// dlLinkedList.add(goodsNode1);
// dlLinkedList.add(goodsNode2);
// dlLinkedList.add(goodsNode3);
// dlLinkedList.add(goodsNode4);
dlLinkedList.addOrder(goodsNode2);
dlLinkedList.addOrder(goodsNode4);
dlLinkedList.addOrder(goodsNode1);
dlLinkedList.addOrder(goodsNode3);
dlLinkedList.add(new GoodsNode(8,"特步鞋子",333.9));
dlLinkedList.addOrder(new GoodsNode(5,"鸿星尔克鞋子",111.9));
dlLinkedList.updateNode(new GoodsNode(8,"特步鞋子",444.9));
dlLinkedList.delNode(8);
dlLinkedList.list();
}
}
);
dlLinkedList.add(new GoodsNode(8,"特步鞋子",333.9));
dlLinkedList.addOrder(new GoodsNode(5,"鸿星尔克鞋子",111.9));
dlLinkedList.updateNode(new GoodsNode(8,"特步鞋子",444.9));
dlLinkedList.delNode(8);
dlLinkedList.list();
}
}