GitHub同步更新(已分类):Data_Structure_And_Algorithm-Review
公众号:URLeisure 的复习仓库
公众号二维码见文末
以下是本篇文章正文内容,下面案例可供参考。
什么是链表?
- 链表是线性表的链式存储方式。
- 逻辑上相邻的数据在计算机内的存储位置不一定相邻。
- 链表由节点组成,每个节点都包含下一个节点的指针。
单链表的存储方式(图解)
-
给每个元素附加一个指针域,指向下一个元素的存储位置。
-
由图可以看出,每个节点包含两个域:数据域和指针域。
-
数据域存储元素,指针域存储下一个节点的地址,因此指针指向的类型也是节点类型。
-
每个指针都指向下一个节点,都是朝一个方向的,这样的链表称为单向链表或单链表。
-
脑里有图的话,链表学起来还是很简单的。
单链表的基本操作
- 首先定义一个结构体(内部类),包含数据域与指针域。
c++代码如下(示例):
typedef struct LNode{
int date;
LNode *next;
}*LinkList;
java代码如下(示例):
private class LNode{
int date;
LNode next;
}
1.初始化
- 单链表的初始化是指构建一个空表。
- 先创建一个头节点,不存储数据,然后令其指针域为空(判断终止时会用到)。
c++代码如下(示例):
bool InitList(LinkList &L){
L = new LNode;//生成新节点
if(!L){//鲁棒
return false;
}
L->date = 0;
L->next = NULL;//下一个节点为空
return true;
}
java代码如下(示例):
public boolean init(){
lnode = new LNode(
if(lnode==null){
return false;
}
lnode.next = null;
return true;
}
2.创建
-
创建单链表分为头插法和尾插法两种。
-
头插法是指每次把新节点插到头节点之后,其创建的单链表和数据输入顺序正好相反,因此也称为逆序建表。
-
头插法,就是新指针插到头指针后面。也就是上次插入元素的前面。
-
很明显可以看出,输出顺序与输入顺序相反。有一种栈(后进先出)的感觉,后面栈链的实现也是使用的这种方法。
c++代码如下(示例):
void CreateList_H(LinkList &L,int n){
LinkList s;
while(n--){
s = new LNode;
cin>>s->date;
//新节点next替换头节点next
s->next = L->next;
L->next = s;
}
}
java代码如下(示例):
public void create_H(int n){
LNode s;
while(n-- > 0){
s = new LNode();
s.date = input.nextInt();
s.next = lnode.next;
lnode.next = s;
}
}
- 尾插法是指每次把新节点链接到链表尾部,其创建的单链表和数据输入顺序一致,因此也称为正序建表。
-
尾插法,将新节点链接到尾节点(r)后面,非常的简单。
-
注意新节点的指针域与尾结点的更新。
c++代码如下(示例):
void CreateList_R(LinkList &L,int n){
LinkList s;
LinkList r = L;
while(n--){
s = new LNode;
cin>>s->date;
s->next = NULL;
r->next = s;//节点连上
r = s;//更新尾节点
}
}
java代码如下(示例):
public void create_R(int n){
LNode s,r = lnode;
while(n-- > 0){
s = new LNode();
s.date = input.nextInt();
s.next = null;
r.next = s;
r = s;
}
}
3.取值
-
单链表的取值不像顺序表那样可以随机访问任何一个元素,单链表只有头指针,各个节点的物理地址是不连续的。
-
要想找到第 i 个节点,就必须从第一个节点开始顺序向后找,一直找到第 i 个节点。
c++代码如下(示例):
bool GetElem(LinkList L,int i,int &e) {//i 查看第i个元素 e为此元素的值
LinkList p = L->next;
int j = 1;
while (p && j < i) {
p = p->next;
j++;
}
if (!p) {
return false;
}
e = p->date;
return true;
}
java代码如下(示例):
public int get(int i){
LNode p = lnode.next;
int j = 1;
while(p!=null && j<i){
p = p.next;
j++;
}
if(p==null && j>i){
return -1;
}
return p.date;
}
4.查找
- 查找是否存在元素 e,可以定义一个 p 指针,指向第一个元素节点,比较 p 指向节点的数据域是否等于 e。
c++代码如下(示例):
bool LocateElem(LinkList L,int e){
LinkList p = L->next;
while(p && p->date!=e){
p = p->next;
}
if(!p)//不存在e时,p == NULL
return false;
return true;
}
java代码如下(示例):
public int locate(int e){
LNode p = lnode.next;
int i = 1;
while(p!=null && p.date!=e){
p = p.next;
i++;
}
if(p == null){
return -1;
}
return i;
}
5.插入
-
要在第i个节点前插入一个元素,则必须先找到第i-1个节点。
-
因为单链表只有一个指针域,是向后操作的,无法向前操作。
c++代码如下(示例):
bool ListInsert(LinkList &L,int i,int e){//第i个位置插入元素e
LinkList p = L;
int j = 0;
while(p && j<i-1){//寻找前一个
p = p->next;
j++;
}
if(!p || j>i-1){//防止i超出范围(i<1 || i>length)
return false;
}
LinkList s = new LNode;
s->date = e;
s->next = p->next;
p->next = s;
return true;
}
java代码如下(示例):
public boolean insert(int i,int e){
LNode s,p = lnode;
int j = 0;
while(p!=null && j<i-1){
p = p.next;
j++;
}
if(p==null || j>i-1){
return false;
}
s = new LNode();
s.date = e;
s.next = p.next;
p.next = s;
return true;
}
6.删除
-
删除一个节点,实际上是把这个节点跳过去。
-
根据点链表向后操作的特性,要跳过第i个节点,就必须先找到第i-1个节点。
-
删除同时,c++需要释放内存。
c++代码如下(示例):
bool ListDelete(LinkList &L,int i,int &e){//删除第i个位置 记录删除的元素值e
LinkList p = L;
int j = 0;
while(p && j<i-1){
p = p->next;
j++;
}
if(!p || j>i-1){//防止i超出范围(i<1 || i>length)
return false;
}
LinkList q = p->next;
p->next = q->next;
e = q->date;
delete q;
return true;
}
java代码如下(示例):
public int delete(int i){
LNode q,p = lnode;
int j = 0;
while(p!=null && j<i-1){
p = p.next;
j++;
}
if(p==null || j>i-1){
return -1;
}
q = p.next;
p.next = q.next;
return q.date;
}
7.打印
- 打印非常简单,直接上代码。
c++代码如下(示例):
void PrintList(LinkList L){
LinkList p = L->next;
while(p){
cout<<p->date<<" ";
p = p->next;
}
cout<<endl;
}
java代码如下(示例):
public void print(){
LNode p = lnode.next;
while(p != null){
System.out.print(p.date+" ");
p = p.next;
}
System.out.println();
}
8.释放内存
- c++需要手动释放内存,而java不需要。
c++代码如下(示例):
void DestroyList(LinkList &L){
LinkList tmp;
for(LinkList cur = L->next;cur!=NULL;){
tmp = cur;
cur = cur->next;
delete tmp;
}
delete L;
}
完整代码
c++代码如下(示例):
#include<iostream>
using namespace std;
typedef struct LNode {
int date;
LNode *next;
} *LinkList;
bool InitList(LinkList &L) {
L = new LNode;
if (!L) {
return false;
}
L->next = NULL;
return true;
}
void CreateList_H(LinkList &L) {
printf("输入要插入的个数\n");
int n;
cin >> n;
LinkList L1;
printf("输入elem\n");
while (n--) {
L1 = new LNode;
cin >> L1->date;
L1->next = L->next;
L->next = L1;
}
}
void CreateList_R(LinkList &L) {
int n;
cin >> n;
LinkList s, r;
r = L;
while (n--) {
s = new LNode;
cin >> s->date;
s->next = NULL;
r->next = s;
r = s;
}
}
bool GetElem(LinkList L, int i, int &e) {
int j = 1;
LinkList p;
p = L->next;
while (j < i && p) {
p = p->next;
j++;
}
if (p == NULL) {
return -1;
}
e = p->date;
return true;
}
bool LocateElem_L(LinkList L, int e) {
LinkList p = L->next;
while (p && p->date != e) {
p = p->next;
}
if (!p) {
return false;
}
return true;
}
bool ListInsert_L(LinkList &L, int i, int e) {
LinkList p = L;
int j = 0;
while (p && j < i - 1) {
p = p->next;
j++;
}
if (!p) {
return false;
}
LinkList n = new LNode;
n->date = e;
n->next = p->next;
p->next = n;
return true;
}
bool ListDelete_L(LinkList &L, int i, int &e) {
LinkList p = L, q;
int j = 0;
while (p && j < i - 1) {
p = p->next;
j++;
}
if (!p) {
return false;
}
q = p->next;
p->next = q->next;
delete q;
return true;
}
void ListPrint_L(LinkList L) {
LinkList p = L->next;
while (p) {
printf("%d ", p->date);
p = p->next;
}
puts("");
}
void ListFree_L(LinkList L) {
LinkList temp;
for (LinkList cur = L->next; cur != NULL;) {
temp = cur;
cur = cur->next;
delete temp;
temp = NULL;
}
L = NULL;
}
int main() {
LinkList L;
int i, e, x;
InitList(L);//初始化表
CreateList_H(L);//头插法
//CreateList_R(L);//尾插法
cin >> i;//取值
GetElem(L, i, e);
cout << "第" << i << "个元素是" << e << endl;
cin >> x;//查找
if (LocateElem_L(L, x)) {
cout << "查找成功" << endl;
} else {
cout << "查找失败" << endl;
}
cin >> i >> e;//插入 位置-元素
ListInsert_L(L, i, e);
cin >> i;//删除
ListDelete_L(L, i, e);
cout << "已删除" << e << endl;
ListPrint_L(L);//打印
ListFree_L(L);//释放空间
return 0;
}
java代码如下(示例):
import java.util.Scanner;
public class A {
public static class LNode {
int date;
LNode next;
}
public static boolean init(LNode L) {
L = new LNode();
L.next = null;
return true;
}
public static void create_h(LNode L) {
Scanner sc = new Scanner(System.in);
System.out.println("输入几个?");
int n = sc.nextInt();
LNode s;
while (n > 0) {
s = new LNode();
s.date = sc.nextInt();
s.next = L.next;
L.next = s;
n--;
}
}
public static void create_r(LNode L) {
Scanner sc = new Scanner(System.in);
System.out.println("输入几个?");
int n = sc.nextInt();
LNode p = L, s;
while (n > 0) {
s = new LNode();
s.date = sc.nextInt();
s.next = null;
p.next = s;
p = s;
n--;
}
}
public static int get(LNode L, int i) {
LNode p = L.next;
int j = 1;
while (p != null && j < i) {
p = p.next;
j++;
}
if (p == null) {
return -1;
}
return p.date;
}
public static int locate(LNode L, int e) {
LNode p = L.next;
int i = 1;
while (p != null && p.date != e) {
p = p.next;
i++;
}
if (p == null) {
return -1;
}
return i;
}
public static boolean insert(LNode L, int i, int e) {
LNode p = L;
LNode s = new LNode();
int j = 0;
while (p != null && j < i - 1) {
p = p.next;
j++;
}
if (p == null) {
return false;
}
s.date = e;
s.next = p.next;
p.next = s;
return true;
}
public static boolean delete(LNode L, int i) {
LNode p = L;
LNode s;
int j = 0;
while (p != null && j < i - 1) {
p = p.next;
j++;
}
if (p == null) {
return false;
}
s = p.next;
p.next = s.next;
return true;
}
public static void print(LNode L) {
LNode p = L.next;
while (p != null) {
System.out.print(p.date + " ");
p = p.next;
}
}
public static void main(String[] args) {
LNode L = new LNode();
Scanner sc = new Scanner(System.in);
int e, i;
init(L);
System.out.println("1.头插法\n2.尾插法");
int ch = sc.nextInt();
if (ch == 1) {
create_h(L);
} else {
create_r(L);
}
i = sc.nextInt();//取值
e = get(L, i);
System.out.println("第" + i + "个元素为" + e);
e = sc.nextInt();//查询
i = locate(L, e);
System.out.println(e + "是第" + i + "个元素!");
//插入 位置-元素
i = sc.nextInt();
e = sc.nextInt();
insert(L, i, e);
i = sc.nextInt();//删除
delete(L, i);
print(L);
}
}
总结
- 在单链表中,每个节点除了存储自身数据之外,还存储了下一个节点的地址,因此可以轻松访问下一个节点,以及后面的所有后继节点。
- 但是,如果想访问前面的节点就不行了,是回不去的。
- 如果要向前操作,就需要用下下下一篇文章的另外一种链表——双向链表。
关注公众号,感受不同的阅读体验
下期预告: 顺序队列-单向队列