【数据结构】单链表&&静态链表详解和代码实例

喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号【程序猿声】

分享图片

01 单链表(Singly Linked List )

1.1 什么是单链表?

单链表是一种链式存储的结构。它动态的为节点分配存储单元。当有节点插入时,系统动态的为结点分配空间。在结点删除时,应该及时释放相应的存储单元,以防止内存泄露。由于是链式存储,所以操作单链表时,必须知道头结点或者头指针的位置。并且,在查找第i个节点时,必须找到第i-1个节点。

1.2 单链表的存储结构代码描述

对于链式存储,通过上一节的讲解相信大家已经了解得够清楚了。如下图所示:

分享图片

下面我们来看看单链表存储是如何用代码来实现的。

1//单链表的存储结构C语言代码
2typedef struct SListNode
3{

4    datatype data;    //数据域
5    struct SListNode * pnext;//指针域
6}SLinkList;

由上面的结构我们可以看出,一个节点由存放数据的数据域和存放地址的指针域组成。假如p指向了第i个节点,那么p->data就是该节点存放的数据,而p->pnext自然就是指向下一个节点的指针。如下图所示:

分享图片

那么接下来我们看看单链表的各个操作具体实现吧。(只讲几个关键步骤)
备注:下面的代码基于这样的一个单链表:

  • 有一个头指针phead
  • 有一个头结点node
  • 头指针指向头结点,头结点位置记为0

1.3 单链表的读取

在拿到头指针以后,单链表的读取也并非一件难事。一开始设置一个计数变量,不断遍历链表,让计数器自增。找到合适的位置将数据读取出来。具体代码实现如下:

 1#define status bool
2#define ERROR false
3#define OK true
4/*
5 * 函数功能:获取位置index节点的数据
6 * 参数说明:phead链表头结点,e用来获取的变量,index索引
7*/

8
9status GetSListIndexNode(Node * phead,DType *e, int index)
10
{
11    int icount = 0//计数器
12    //注:0号位为头结点,头结点不存放任何数据
13    if (phead->pnext == nullptr || index < 1 || index > GetSListLength()/*此处为链表长度*/)
14    {
15        return ERROR; //异常 处理
16    }
17    while (phead->pnext != nullptr)
18    {
19        icount++;
20        phead = phead->pnext;
21        if (icount == index)
22        {
23            *e = phead->data;
24            return OK;
25        }
26    }
27    return ERROR;
28}

1.4 单链表的插入

1.4.1 指定位置后插

其实链表的插入和删除都是很简单的操作,初学者只要抓住指针指向的节点,并加以区分开来,就很easy了。如下图:

分享图片

图中,假如此时p指向了我们要插入的节点的位置。那么,怎样把我们的S节点给插入到p指向的节点之后?在这里我们先不要惊动p以及p后面的节点:

  1. 我们先让S节点指向p之后的节点(步骤①)
  2. 之后我们切断p和p后面那个节点的关系(步骤②)
  3. 最后让p节点的指针域指向s节点(步骤③),搞定

算法描述:

  1. 声明一个指针p指向链表头结点,向后遍历p=p->next,找到正确的位置。
  2. 新建一个结点s。
  3. s->next = p->next ①
  4. p->next = s ②③
    具体代码如下:
 1#define status bool
2#define ERROR false
3#define OK true
4/*
5 * 函数功能:指定位置后插
6 * 参数说明:phead链表头结点,IData插入的数据,index索引
7*/

8status InsertSListNodeFront(Node * phead, DType IData, int index)
9
{
10    if (phead->pnext == nullptr || index < 1 || index > GetSListLength())
11    {
12        return ERROR; //异常 处理
13    }
14    int iCount = 0//计数器
15    Node<DType> * q = nullptr//备用
16    while (phead->pnext != nullptr)
17    {
18        iCount++;
19        q = phead;
20        phead = phead->pnext;
21        if ( iCount == index )
22        {
23            Node<DType> * p = new Node<DType>;
24            p->data = IData;
25            p->pnext = phead;
26            q->pnext = p;   //前插
27            return OK;
28        }
29    }
30    return ERROR;
31}
1.4.2 指定位置前插

咳咳,聪明的小伙伴,用脑子想想。指定位置前插 == 指定位置的前一个位置进行后插。懂了吧?直接看具体代码:

 1/*
2 * 函数功能:指定位置后插
3 * 参数说明:phead链表头结点,IData插入的数据,index索引
4*/

5status InsertSListNodeBack(Node * phead, int index)
6
{
7    if (phead->pnext == nullptr || index < 1 || index > GetSListLength())
8    {
9        return ERROR; //异常 处理
10    }
11    int iCount = 0//计数器
12    Node<DType> * q = nullptr//备用
13    while (phead->pnext != nullptr)
14    {
15        iCount++;
16        q = phead;
17        phead = phead->pnext;
18        if (iCount == index)
19        {
20            Node<DType> * p = new Node<DType>;
21            q = phead;
22            phead = phead->pnext; //后插就是后一个节点的前插咯
23            p->data = IData;
24            p->pnext = phead;
25            q->pnext = p;   
26            return OK;
27        }
28    }
29    return ERROR;
30}

1.5 单链表的删除

单链表的删除其实也是很简单。只要比如要删除p指向的节点,只需要让p之前的节点的指针域直接指向p之后的节点,再把p给free就OK了。如下图:

分享图片

算法描述:

  1. 声明一个指针p指向链表头结点,向后遍历p=p->next,找到要删除的节点位置。
  2. q = p->next
  3. p->next = q->next ①②
  4. free(q) ③④

具体代码如下:

 1/*
2 * 函数功能:指定位置后插
3 * 参数说明:phead链表头结点,IData获取删除的数据,index索引
4*/

5//删除指定位置节点(e获取删除元素)
6template <typename DType>
7status DeleteSListIndexNode(Node * phead, DType *e, int index)
8
{
9    int i = 0//计数器
10    Node<DType> * q = nullptr;
11    if (phead->pnext == nullptr || index < 1 || index > GetSListLength())
12    {
13        return ERROR; //异常 处理
14    }
15    while (phead->pnext != nullptr)
16    {
17        i++;
18        q = phead; //保存备用
19        phead = phead->pnext;
20        if (i == index)
21        {
22            *e = phead->data;
23            q->pnext = phead->pnext; //删除出局
24            return OK;
25        }
26    }
27    return ERROR;
28}

代码应该不难,相信大家都能很容易看懂。

1.6.1 单链表的完整代码

好了,前面介绍了几个重要的操作,接下来请大家看看完整的代码吧。小编为了使用方便,就用C++的class和template将整个链表封装到了一个类里面,通过模板实现泛型编程。

 1/*
2 * 文件名:SingleLinkList.h
3 * 说明 :类的各种声明
4 */

5#pragma once //VC编译器防止头文件被重复包含的一条预编译指令
6
7#define status bool
8#define OK true
9#define ERROR false
10#define YES true
11#define NO false
12
13template <typename DType>
14class Node
15{

16public:
17    DType data;
18    Node * pnext;
19};
20
21template <typename DType>
22class CSingleLinkList
23{

24private:
25    Node<DType> *phead; //链表头指针
26public:
27    CSingleLinkList();//构造,类被创建时调用
28    ~CSingleLinkList();//析构,类被销毁时调用
29public:
30    //初始化链表
31    status InitSList();
32    //获取链表长度
33    int GetSListLength();
34    //增加一个节点 前插法
35    status AddSListNodeFront(DType idata);
36    //增加一个节点 后插法
37    status AddSListNodeBack( DType idata);
38    //判断链表是否为空
39    status IsSListEmpty();
40    //获取指定位置节点值(注意,本程序规定0号为头节点,e获取删除元素)
41    status GetSListIndexNode(DType *e, int index);
42    //删除指定位置节点(e获取删除元素)
43    status DeleteSListIndexNode(DType *e, int index);
44    //查找链表中指定值(pindex获取位置0==>not found)
45    status SearchSListNode(DType SData, int *pindex);
46    //指定位置前插
47    status InsertSListNodeFront(DType IData, int index);
48    //指定位置后插
49    status InsertSListNodeBack(DType IData, int index);
50    //清空链表(保留根节点)
51    status ClearSList();
52    //销毁链表(all delete)
53    status DestorySList();
54    //尾部删除一个元素
55    status DeleteSListNodeBack();
56    //打印链表   此函数根据实际情况而定
57    void PrintSList();
58};
59
60/*
61 * 文件名:SingleLinkList.cpp
62 * 说明 :类的各种方法的实现
63 */

64
65#include "SingleLinkList.h"
66#include <stdio.h>
67
68template <typename DType>
69CSingleLinkList<DType>::CSingleLinkList()
70{
71    cout << "链表创建" << endl;
72    InitSList();
73}
74template <typename DType>
75CSingleLinkList<DType>::~CSingleLinkList()
76{
77    cout << "链表销毁" << endl;
78    DestorySList();
79}
80//初始化链表
81template <typename DType>
82status CSingleLinkList<DType>::InitSList()
83{
84    Node<DType> * ph = new Node<DType>;
85    if (ph != NULL)
86    {
87        ph->pnext = nullptr;
88        this->phead = ph; //头结点
89        return OK;
90    }
91    return ERROR;
92}
93//获取链表长度(head_node is not included)
94template <typename DType>
95int CSingleLinkList<DType>::GetSListLength()
96{
97    int length = 0;
98    Node<DType> * phead = this->phead;
99    while (phead->pnext != nullptr)
100    {
101        length++;
102        phead = phead->pnext;
103    }
104    return length;
105}
106//增加一个节点 前插法
107template <typename DType>
108status CSingleLinkList<DType>::AddSListNodeFront( DType idata)
109{
110    Node<DType> * pnode = new Node<DType>;
111    if (pnode != NULL)
112    {
113        pnode->data = idata;
114        pnode->pnext = this->phead->pnext;
115        this->phead->pnext = pnode; //挂载
116
117        //printf("pnode = %p  pnode->pnext = %p this->phead->pnext = %p this->phead = %p\n", pnode, pnode->pnext, this->phead->pnext, this->phead);
118        return OK;
119    }
120    return ERROR;
121}
122
123
124//增加一个节点 尾插法
125template <typename DType>
126status CSingleLinkList<DType>::AddSListNodeBack(DType idata)
127{
128    Node<DType> * pnode = new Node<DType>;
129    Node<DType> * phead = this->phead;
130    if (pnode != NULL)
131    {
132        while (phead->pnext != nullptr)
133        {
134            phead = phead->pnext;
135        }
136        pnode->data = idata;
137        pnode->pnext = nullptr;
138        phead->pnext = pnode; //挂载
139        return OK;
140    }
141    return ERROR;
142}
143//判断链表是否为空
144template <typename DType>
145status CSingleLinkList<DType>::IsSListEmpty()
146{
147    if (this->phead->pnext == nullptr)
148    {
149        return YES;
150    }
151    return NO;
152}
153//获取指定位置节点值(注意,本程序规定0号为头节点,e获取节点的值)
154template <typename DType>
155status CSingleLinkList<DType>::GetSListIndexNode(DType *e, int index)
156{
157    Node<DType> * phead = this->phead;
158    int i = 0//计数器
159    if (phead->pnext == nullptr || index < 1 || index > GetSListLength())
160    {
161        return ERROR; //异常 处理
162    }
163    while (phead->pnext != nullptr)
164    {
165        i++;
166        phead = phead->pnext;
167        if (i == index)
168        {
169            *e = phead->data;
170            return OK;
171        }
172    }
173    return ERROR;
174}
175//删除指定位置节点(e获取删除元素)
176template <typename DType>
177status CSingleLinkList<DType>::DeleteSListIndexNode( DType *e, int index)
178{
179    Node<DType> * phead = this->phead;
180    int i = 0//计数器
181    Node<DType> * q = nullptr;
182    if (phead->pnext == nullptr || index < 1 || index > GetSListLength())
183    {
184        return ERROR; //异常 处理
185    }
186    while (phead->pnext != nullptr)
187    {
188        i++;
189        q = phead; //保存备用
190        phead = phead->pnext;
191        if (i == index)
192        {
193            *e = phead->data;
194            q->pnext = phead->pnext; //删除出局
195            return OK;
196        }
197    }
198    return ERROR;
199}
200//查找链表中指定值(pindex获取位置   0==>not found)
201template <typename DType>
202status CSingleLinkList<DType>::SearchSListNode( DType SData, int *pindex)
203{
204    Node<DType> * phead = this->phead;
205    int iCount = 0;//计数器
206    while (phead->pnext != nullptr)
207    {
208        iCount++;
209        phead = phead->pnext;
210        if (phead->data == SData)
211        {
212            *pindex = iCount;
213            return YES;
214        }
215    }
216    *pindex = 0;
217    return NO;
218}
219//指定位置前插
220template <typename DType>
221status CSingleLinkList<DType>::InsertSListNodeFront(DType IData, int index)
222{
223    Node<DType> * phead = this->phead;
224    if (phead->pnext == nullptr || index < 1 || index > GetSListLength())
225    {
226        return ERROR; //异常 处理
227    }
228    int iCount = 0//计数器
229    Node<DType> * q = nullptr//备用
230    while (phead->pnext != nullptr)
231    {
232        iCount++;
233        q = phead;
234        phead = phead->pnext;
235        if ( iCount == index )
236        {
237            Node<DType> * p = new Node<DType>;
238            p->data = IData;
239            p->pnext = phead;
240            q->pnext = p;   //前插
241            return OK;
242        }
243    }
244    return ERROR;
245}
246//指定位置后插
247template <typename DType>
248status CSingleLinkList<DType>::InsertSListNodeBack( DType IData, int index)
249{
250    Node<DType> * phead = this->phead;
251    if (phead->pnext == nullptr || index < 1 || index > GetSListLength())
252    {
253        return ERROR; //异常 处理
254    }
255    int iCount = 0//计数器
256    Node<DType> * q = nullptr//备用
257    while (phead->pnext != nullptr)
258    {
259        iCount++;
260        q = phead;
261        phead = phead->pnext;
262        if (iCount == index)
263        {
264            Node<DType> * p = new Node<DType>;
265            q = phead;
266            phead = phead->pnext; //后插就是后一个节点的前插咯
267            p->data = IData;
268            p->pnext = phead;
269            q->pnext = p;   
270            return OK;
271        }
272    }
273    return ERROR;
274}
275//清空链表(保留根节点)
276template <typename DType>
277status CSingleLinkList<DType>::ClearSList()
278{
279    Node<DType> * phead = this->phead;
280    Node<DType> * q = nullptr//防止那啥,野指针
281    phead = phead->pnext;//保留头节点
282    while (phead != nullptr)
283    {
284        q = phead;
285        phead = phead->pnext;
286        delete q; //释放
287    }
288    this->phead->pnext = nullptr;
289    return OK;
290}
291//销毁链表(all delete)
292template <typename DType>
293status CSingleLinkList<DType>::DestorySList()
294{
295    ClearSList();
296    delete this->phead;//释放头结点 
297    return OK;
298}
299
300template <typename DType>
301status CSingleLinkList<DType>::DeleteSListNodeBack()
302{
303    Node<DType> * phead = this->phead;
304    Node<DType> * q = nullptr//备用
305    if (phead->pnext == nullptr)
306    {
307        return ERROR; //链表都空了还删鸡毛
308    }
309    while (phead->pnext != nullptr)
310    {
311        q = phead;
312        phead = phead->pnext;
313    }
314    q->pnext = nullptr;
315    delete phead;
316
317    return OK;
318
319}
320template <typename DType>
321void CSingleLinkList<DType>::PrintSList()
322{
323    Node<DType> * phead = this->phead;
324    if (phead->pnext == nullptr || phead == nullptr)
325    {
326        cout << "链表为空,打印鸡毛" << endl;
327        return;
328    }
329    int i = 1;
330    cout << "===============print list================" << endl;
331    while (phead->pnext != nullptr)
332    {
333        phead = phead->pnext;
334        cout <<"node[" << i++ << "] = " << phead->data<<endl;
335    }
336}
337
338/*
339 * 文件名:SingleLinkListTest.cpp
340 * 说明 :测试代码
341 */

342
343 #include <iostream>
344#include "SingleLinkList.h"
345#include "SingleLinkList.cpp"
346
347using namespace std;
348
349int main()
350
{
351    CSingleLinkList<int> * pMySList = new CSingleLinkList<int>;
352    cout << pMySList->IsSListEmpty() << endl;
353    pMySList->AddSListNodeFront(111);
354    pMySList->AddSListNodeFront(222);
355    pMySList->AddSListNodeFront(333);
356
357    cout << "链表长度" << pMySList->GetSListLength() << endl;
358
359    pMySList->PrintSList();
360
361    pMySList->AddSListNodeBack(444);
362    pMySList->AddSListNodeBack(555);
363    pMySList->AddSListNodeBack(666);
364
365    pMySList->PrintSList();
366
367    cout << pMySList->IsSListEmpty() << endl;
368    cout << "链表长度" << pMySList->GetSListLength() << endl;
369
370    int GetElem, GetIndex;
371    pMySList->GetSListIndexNode(&GetElem, 2);
372    cout << "Got Elem = " << GetElem << endl;
373
374    pMySList->GetSListIndexNode(&GetElem, 6);
375    cout << "Got Elem = " << GetElem << endl;
376
377    pMySList->GetSListIndexNode(&GetElem, 4);
378    cout << "Got Elem = " << GetElem << endl;
379
380    pMySList->DeleteSListIndexNode(&GetElem, 1);
381    cout << "del Elem = " << GetElem << endl;
382    pMySList->DeleteSListIndexNode(&GetElem, 3);
383    cout << "del Elem = " << GetElem << endl;
384
385
386    pMySList->PrintSList();
387
388    pMySList->SearchSListNode(555, &GetIndex);
389    cout << "Search Index = " << GetIndex << endl;
390    pMySList->SearchSListNode(111, &GetIndex);
391    cout << "Search Index = " << GetIndex << endl;
392    pMySList->SearchSListNode(666, &GetIndex);
393    cout << "Search Index = " << GetIndex << endl;
394
395    pMySList->InsertSListNodeFront(3331);
396    pMySList->InsertSListNodeFront(4444);
397
398    pMySList->PrintSList();
399
400    pMySList->InsertSListNodeBack(7773);
401    pMySList->InsertSListNodeBack(8885);
402
403    pMySList->PrintSList();
404
405    pMySList->DeleteSListNodeBack();
406    pMySList->PrintSList();
407    pMySList->DeleteSListNodeBack();
408    pMySList->PrintSList();
409    pMySList->DeleteSListNodeBack();
410    pMySList->PrintSList();
411
412    return 0;
413}
414
415

代码如果有不正确的地方,欢迎大家来指正哈。

02 静态链表(circular linked list)

2.1 什么是静态链表?

我们把线性表的元素存放在数组中,这些元素由两个域组成:

  • 数据域data
  • 指针域cur

数据域是存放数据的,而指针域,这里和链表不同是,它存的不再是指向下一个节点的内存地址。而是下一个节点在数组中的下标。我们就把这种用数组描述的链表称为静态表,该方法也称之为游标实现法。如下图所示:

分享图片

由上图我们需要注意以下几点:

  • 我们对数组的第一个元素和最后一个元素做特殊处理,不存放数据。
  • 把未使用的数组元素称为备用链表。
  • 数组的第一个元素(下标为0)的cur域存放备用链表第一个节点的下标。
  • 数组的最后一个元素的cur域存放第一个有数据的节点的下标,相当于链表中头结点的存在。链表为空时,其值为0。

如下图:

分享图片

引出的问题:数组的长度定义的问题,无法预支。所以,为了防止溢出,我们一般将静态表开得大一点。

2.2 静态链表存储的代码描述

基于上面的讲解,我们来看看代码是怎么描述这种存储结构的。

1//---------线性表的静态单链表存储结构--------
2#define MAXSIZE 1000 /*假设链表最大长度为1000*/
3typedef struct
4{

5    datatype data;
6    int cur;   //为0时表示无指向
7}SLinkList[MAXSIZE];

接下来我们讲解几个重要的操作实现。

2.3 静态链表的插入操作

前面我们讲动态链表的时候说过,增加和删除一个节点我们可以用malloc()和free()函数(C++可用new和delete)来实现。但是现在由于我们操作的是静态表,它可是用数组存的,可没有这种操作了。因此我们首先来自己实现一个静态表的malloc和free。

那么怎么辨别数组中哪些空间没有被使用呢?一个好的解决办法是,将所有未使用或者被删除的空间串成一个备用链表。插入节点时便可以从备用链表获取第一个未使用的空间的下标。因此我们在初始化的时候会做这样的工作:

 1void SListInit(SLinkList space)
2
{
3    int i;
4    for(i = 0; i < MAXSIZE; i++)
5        space[i].cur = i+1//将所有结点链入备用链表
6    //备用链表头指针链像第二个结点
7    space[0].cur = space[1].cur; 
8    //第一个结点作为链表的头结点  
9    space[1].cur = 0;              
10}

分配内存

 1/分配备用链表的一个结点,返回下标
2//若为0,则说明备用链表用完
3int Malloc_SL(SLinkList space)
4
{
5    int i = space[0].cur;
6    //判断备用链表是否非空
7    if(space[0].cur)    
8        //备用链表头指针指向第二个空结点
9        space[0].cur = space[i].cur; 
10
11    return i;    //返回第一个空结点
12}

上面的代码应该是没有难度的。写完了这个函数,我们来看看静态表中具体如何插入:

 1//在链表的第i个位置插入元素e
2void SlistInsert(SLinkList space, int i, ElemType e)
3
{
4    //超出范围
5    if(i < 1 || i > SListLength(space)+1
6        return;
7    int k = 1, j;
8    //使k指示要插入的结点的前一个结点
9    for(j = 0; j <i-1; j++) 
10        k = space[k].cur;
11
12    int v = Malloc_SL(space);
13    if(v)     //如果有空间
14    {
15        space[v].data = e;
16        space[v].cur = space[k].cur;
17        space[k].cur = v;  //链入链表
18    }
19}

注意几点:

  • 首先我们让k指向了要插入节点(记为X)的前一个位置(记为Y节点),前插法。
  • 然后我们在静态表内申请空间,存放新的节点(记为N)。
  • 把数据放进新申请的节点里面。
  • 新节点N的cur域指向X节点,然后让Y节点指向N节点。

该过程不难理解。就不上图了……

2.4 静态链表的删除操作

删除同样需要自己实现free函数,我们来看看代码:

回收内存

1//将下标为k的空闲结点回收到备用链表
2void Free_SL(SLinkList space, int k)
3
{
4    //将备用链表链到k之后
5    space[k].cur = space[0].cur;  
6    //将k链到备用链表头之后
7    space[0].cur = k;               
8}

删除以后记得把空间重新挂载到备用链表上以便下一次使用。下面我们实现删除的代码:

 1//删除第i个位置的元素
2void SListDelete(SLinkList space, int i)
3
{   //超出范围退出
4    if(i < 1 || i > SListLength(space))  
5        return ;
6    int k = 1, j;
7     //使k指示要删除的结点的前一个结点
8    for(j = 0; j <i-1; j++)  
9        k = space[k].cur;
10
11    int temp = space[k].cur;
12    space[k].cur = space[temp].cur;
13    Free_SL(space, temp);
14}

其实代码也很好理解了。假如X、Y、Z……等等排列,我们要删除Y。无非就是告诉X,你不要指向Y了,你直接指向Z,然后我们再把Y给free了。就直接变成了X、Z……简单吧。

2.5 静态链表的完整代码

熬了一个下午,终于写完了.哈哈,用了C++的类模板封装了一个静态链表,简单的增删查改功能都有了.具体可以看代码:

StaticLinkList.h

 1#pragma once
2#include <iomanip>
3
4#define MAXSIZE 100
5
6#define status bool
7#define YES true
8#define NO false
9#define OK true
10#define ERROR false
11
12template <typename DATATYPE>
13class Component
14{

15public:
16    DATATYPE data; //数据域
17    int cur;       //cur域,指向下一个元素的下标
18};
19
20
21template <typename DATATYPE>
22class CStaticLinkList
23{

24public:
25    Component<DATATYPE>  StaticLinkList[MAXSIZE];  //静态表
26//自定义malloc和free
27public:
28    int MallocNodeSSL();
29    status FreeNodeSSL(int index);
30public:
31    status InitList()//初始化静态表
32    status BackAddList( DATATYPE AddData)//尾增加
33    status InsertNodeList(DATATYPE InsertData, int index);//指定位置插入
34    status DeleteNodeList(DATATYPE *DelData, int index)//指定位置删除
35    int SearchList(DATATYPE sData);       //搜索数据为sData的节点,返回其在数组中的下标,0表示失败
36    status GetIndexList(DATATYPE *gData, int index);//获取指定索引的节点数据
37    int GetLengthList()//获取静态表的长度
38    status ClearList()//清空静态表
39    status IsEmptyList()//判断静态表是否为空
40    status IsFullList()//判断静态表是否满了
41    void PrintList()//打印静态表,此函数根据实际情况编写
42
43public:
44    CStaticLinkList();
45    ~CStaticLinkList();
46};

StaticLinkList.cpp

 1#include "StaticLinkList.h"
2
3template <typename DATATYPE>
4CStaticLinkList<DATATYPE>::CStaticLinkList()
5{
6    cout << "===========静态表创建===========" << endl;
7    InitList();
8}
9
10template <typename DATATYPE>
11CStaticLinkList<DATATYPE>::~CStaticLinkList()
12{
13    cout << "===========静态表销毁===========" << endl;
14}
15
16template <typename DATATYPE>
17int CStaticLinkList<DATATYPE>::MallocNodeSSL()
18{
19    int index = StaticLinkList[0].cur; //把备用链表第一个节点拿出来用
20    if (StaticLinkList[0].cur) //判断是否还有位置
21    {
22        StaticLinkList[0].cur = StaticLinkList[index].cur; //让备用链表第二个节点上来顶替第一个的位置
23    }
24
25    return index; //返回0表示分配失败
26}
27
28template <typename DATATYPE>
29status CStaticLinkList<DATATYPE>::FreeNodeSSL(int index)
30{
31    //将删除节点挂接到备用链表上
32    this->StaticLinkList[index].cur = this->StaticLinkList[0].cur;
33    this->StaticLinkList[0].cur = index;
34
35    return OK;
36}
37
38template <typename DATATYPE>
39status CStaticLinkList<DATATYPE>::InitList()
40{
41    int i;
42    for (i = 0; i < MAXSIZE - 1; i++)
43    {
44        StaticLinkList[i].cur = i + 1;//全部塞入备用链表
45    }
46    StaticLinkList[MAXSIZE - 1].cur = 0;/*因为目前静态表为空,最后一个节点的cur域为0*/
47    return OK;
48}
49
50template <typename DATATYPE>
51status CStaticLinkList<DATATYPE>::BackAddList(DATATYPE AddData) //尾增加
52{
53    if (IsFullList())
54    {
55        return ERROR;
56    }
57    int index = MAXSIZE - 1;
58    int last = index;
59
60    while (index != 0)
61    {
62        last = index;
63        index = StaticLinkList[index].cur;
64    }
65
66    int k = MallocNodeSSL(); //获取空闲位置下标
67    if (k)
68    {
69        StaticLinkList[k].data = AddData; //存入数据
70        StaticLinkList[k].cur = 0;   //末尾指向0
71        StaticLinkList[last].cur = k;
72        return OK;
73    }
74
75    return ERROR;
76
77}
78//在List中第i个节点之前插入新的节点
79template <typename DATATYPE>
80status CStaticLinkList<DATATYPE>::InsertNodeList(DATATYPE InsertData, int index)//指定位置插入
81{
82    int i, GetFree, pos;
83    pos = MAXSIZE - 1;//最后节点下标
84    if (index < 1 || index > GetLengthList() || IsFullList())
85    {
86        return ERROR; //位置异常处理
87    }
88
89    GetFree = MallocNodeSSL();
90    if (GetFree)
91    {
92        StaticLinkList[GetFree].data = InsertData;
93        for (i = 0; i < index - 1; i++)
94        {
95            pos = StaticLinkList[pos].cur; //定位
96        }
97        StaticLinkList[GetFree].cur = StaticLinkList[pos].cur;
98        StaticLinkList[pos].cur = GetFree; //插入
99
100        int q = StaticLinkList[MAXSIZE - 1].cur;
101        if (q == 0//静态表为空
102        {
103            StaticLinkList[MAXSIZE - 1].cur = 1;
104        }
105        return OK;
106    }
107    return ERROR;
108}
109
110//判断是否为空
111template <typename DATATYPE>
112status CStaticLinkList<DATATYPE>::IsEmptyList()
113{
114    if (StaticLinkList[MAXSIZE-1].cur == 0)
115    {
116        return YES; 
117    }
118
119    return NO;
120}
121//判断静态表是否满了
122template <typename DATATYPE>
123status CStaticLinkList<DATATYPE>::IsFullList()
124{
125    if (GetLengthList() == MAXSIZE - 2//因为首位不存数据,因此pass掉
126    {
127        return YES;
128    }
129    return NO;
130}
131template <typename DATATYPE>
132int CStaticLinkList<DATATYPE>::GetLengthList() //获取静态表的长度
133{
134    int iCount = 0;
135    int k = MAXSIZE - 1;
136    while (StaticLinkList[k].cur != 0)
137    {
138        iCount++;
139        k = StaticLinkList[k].cur;
140    }
141
142    return iCount;
143}
144template <typename DATATYPE>
145status CStaticLinkList<DATATYPE>::DeleteNodeList(DATATYPE *DelData, int index)//指定位置删除
146{
147    int i, pos, k;
148    pos = MAXSIZE - 1;//最后节点下标
149    if (index < 1 || index > GetLengthList() || IsEmptyList())
150    {
151        return ERROR; //位置异常处理
152    }
153    for (i = 0; i < index - 1; i++)
154    {
155        pos = StaticLinkList[pos].cur; //定位到被删除节点的前一个节点
156    }
157    k = StaticLinkList[pos].cur; 
158    *DelData = StaticLinkList[k].data; //获取数据
159    StaticLinkList[pos].cur = StaticLinkList[k].cur;//让前一个节点直接指向后一个节点.把夹在中间的踢掉
160    FreeNodeSSL(k); //释放空间
161
162    return OK;
163}
164//搜索数据为sData的节点,返回其在静态表中的第i个位置,0表示没找到
165template <typename DATATYPE>
166int CStaticLinkList<DATATYPE>::SearchList(DATATYPE sData)
167{
168    int pos = StaticLinkList[MAXSIZE-1].cur;
169    int iCount = 1;
170    while (pos != 0)
171    {
172        if (StaticLinkList[pos].data == sData) //找到数据
173        {
174            return iCount;
175        }
176        pos = StaticLinkList[pos].cur; //循环遍历
177        iCount++;
178    }
179
180    return 0;
181}
182template <typename DATATYPE>
183status CStaticLinkList<DATATYPE>::GetIndexList(DATATYPE *gData, int index)//获取第index个节点数据
184{
185    int i, pos;
186    pos = MAXSIZE - 1;//最后节点下标
187    if (index < 1 || index > GetLengthList() || IsEmptyList())
188    {
189        return ERROR; //位置异常处理
190    }
191    for (i = 0; i < index; i++)
192    {
193        pos = StaticLinkList[pos].cur; //定位到第index个节点
194    }
195    *gData = StaticLinkList[pos].data;
196
197    return OK;
198}
199template <typename DATATYPE>
200status  CStaticLinkList<DATATYPE>::ClearList() //清空静态表
201{
202    InitList();//再初始化一次就是清空了
203    return  OK;
204}
205template <typename DATATYPE>
206void  CStaticLinkList<DATATYPE>::PrintList()
207{
208    int pos = StaticLinkList[MAXSIZE - 1].cur;
209    if (pos == 0)
210    {
211        cout << "===========静态链表为空,打印鸡毛!!!===========" << endl;
212        return;
213    }
214    cout << setiosflags(ios::left);
215    cout << setw(10) << "索引" << setw(10) << "下标" << setw(10) << "data" << setw(10) << "cur" << endl;
216    int iCount = 1;
217    while (pos != 0)
218    {
219        cout << setw(10) << iCount << setw(10) << pos << setw(10) << StaticLinkList[pos].data << setw(10) << StaticLinkList[pos].cur << endl;
220        pos = StaticLinkList[pos].cur; //循环遍历
221        iCount++;
222    }
223}

StaticLinkListTest.cpp测试代码

 1#include <iostream>
2#include <iomanip>
3#include "StaticLinkList.h"
4#include "StaticLinkList.cpp"
5using namespace std;
6
7int main()
8
{
9    char get;
10    CStaticLinkList<char> *p = new CStaticLinkList<char>;
11    p->PrintList();
12
13    p->BackAddList(‘a‘); //pass
14    p->BackAddList(‘b‘);
15    p->BackAddList(‘c‘);
16    p->BackAddList(‘d‘);
17    p->BackAddList(‘e‘);
18
19    //p->ClearList();      //pass
20
21    p->PrintList();
22
23    p->DeleteNodeList(&get, 2);  //pass
24    cout << "get = " << get << endl;
25
26    p->DeleteNodeList(&get, 2);
27    cout << "get = " << get << endl;
28
29    p->PrintList();
30    p->BackAddList(‘f‘);
31
32    p->PrintList();
33    cout << "length = " << p->GetLengthList() << endl;//pass
34
35    p->GetIndexList(&get, 3);  //pass
36    cout << "get = " << get << endl
37
38    p->InsertNodeList(‘h‘2);
39    p->InsertNodeList(‘i‘3);
40
41    p->PrintList();
42    cout << "length = " << p->GetLengthList() << endl;//pass
43
44    cout << "search_pos = " << p->SearchList(‘q‘) << endl;
45    cout << "search_pos = " << p->SearchList(‘h‘) << endl;
46    cout << "search_pos = " << p->SearchList(‘i‘) << endl;
47
48    delete p;
49
50    return 0;
51}

运行结果

运行结果

运行结果

代码未经过严谨测试,如果有错,欢迎大家指正和交流啊.

这次就先到这里吧。更多精彩内容,请期待下回分解。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


【啊哈!算法】算法3:最常用的排序——快速排序       上一节的冒泡排序可以说是我们学习第一个真正的排序算法,并且解决了桶排序浪费空间的问题,但在算法的执行效率上却牺牲了很多,它的时间复杂度达到了O(N2)。假如我们的计算机每秒钟可以运行10亿次,那么对1亿个数进行排序,桶排序则只需要0.1秒,而冒泡排序则需要1千万秒,达到115天之久,是不是很吓人。那有没有既不浪费空间又可以快一点的排序算法
匿名组 这里可能用到几个不同的分组构造。通过括号内围绕的正则表达式就可以组成第一个构造。正如稍后要介绍的一样,既然也可以命名组,大家就有考虑把这个构造作为匿名组。作为一个实例,请看看下列字符串: “08/14/57 46 02/25/59 45 06/05/85 18 03/12/88 16 09/09/90 13“ 这个字符串就是由生日和年龄组成的。如果需要匹配年两而不要生日,就可以把正则
选择排序:从数组的起始位置处开始,把第一个元素与数组中其他元素进行比较。然后,将最小的元素方式在第0个位置上,接着再从第1个位置开始再次进行排序操作。这种操作一直到除最后一个元素外的每一个元素都作为新循环的起始点操作过后才终止。 public void SelectionSort() { int min, temp;
public struct Pqitem { public int priority; public string name; } class CQueue { private ArrayList pqueue; public CQueue() { pqueue
在编写正则表达式的时候,经常会向要向正则表达式添加数量型数据,诸如”精确匹配两次”或者”匹配一次或多次”。利用数量词就可以把这些数据添加到正则表达式里面了。 数量词(+):这个数量词说明正则表达式应该匹配一个或多个紧紧接其前的字符。 string[] words = new string[] { "bad", "boy", "baad", "baaad" ,"bear", "b
来自:http://blog.csdn.net/morewindows/article/details/6678165/归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列
插入排序算法有两层循环。外层循环会啄个遍历数组元素,而内存循环则会把外层循环所选择的元素与该元素在数组内的下一个元素进行比较。如果外层循环选择的元素小于内存循环选择的元素,那么瘦元素都想右移动以便为内存循环元素留出位置。 public void InsertionSort() { int inner, temp;
public int binSearch(int value) { int upperBround, lowerBound, mid; upperBround = arr.Length - 1; lowerBound = 0; while (lowerBound <= upper
虽然从表内第一个节点到最后一个节点的遍历操作是非常简单的,但是反向遍历链表却不是一件容易的事情。如果为Node类添加一个字段来存储指向前一个节点的连接,那么久会使得这个反向操作过程变得容易许多。当向链表插入节点的时候,为了吧数据复制给新的字段会需要执行更多的操作,但是当腰吧节点从表移除的时候就能看到他的改进效果了。 首先需要修改Node类来为累增加一个额外的链接。为了区别两个连接,这个把指
八、树(Tree)树,顾名思义,长得像一棵树,不过通常我们画成一棵倒过来的树,根在上,叶在下。不说那么多了,图一看就懂:当然了,引入了树之后,就不得不引入树的一些概念,这些概念我照样尽量用图,谁会记那么多文字?树这种结构还可以表示成下面这种方式,可见树用来描述包含关系是很不错的,但这种包含关系不得出现交叉重叠区域,否则就不能用树描述了,看图:面试的时候我们经常被考到的是一种叫“二叉树”的结构,二叉
Queue的实现: 就像Stack类的实现所做的一样,Queue类的实现用ArrayList简直是毋庸置疑的。对于这些数据结构类型而言,由于他们都是动态内置的结构,所以ArrayList是极好的实现选择。当需要往队列中插入数据项时,ArrayList会在表中把每一个保留的数据项向前移动一个元素。 class CQueue { private ArrayLis
来自:http://yingyingol.iteye.com/blog/13348911 快速排序介绍:快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地
Stack的实现必须采用一种基本结构来保存数据。因为再新数据项进栈的时候不需要担心调整表的大小,所以选择用arrayList.using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Collecti
数组类测试环境与排序算法using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Data_structure_and_algorithm{ class CArray { pr
一、构造二叉树 二叉树查找树由节点组成,所以需要有个Node类,这个类类似于链表实现中用到的Node类。首先一起来看看Node类的代码。 public class Node { public int Data; public Node Left; public Node Right; public v
二叉树是一种特殊的树。二叉树的特点是每个结点最多有两个儿子,左边的叫做左儿子,右边的叫做右儿子,或者说每个结点最多有两棵子树。更加严格的递归定义是:二叉树要么为空,要么由根结点、左子树和右子树组成,而左子树和右子树分别是一棵二叉树。 下面这棵树就是一棵二叉树。         二叉树的使用范围最广,一棵多叉树也可以转化为二叉树,因此我们将着重讲解二叉树。二叉树中还有连两种特殊的二叉树叫做满二叉树和
上一节中我们学习了队列,它是一种先进先出的数据结构。还有一种是后进先出的数据结构它叫做栈。栈限定只能在一端进行插入和删除操作。比如说有一个小桶,小桶的直径只能放一个小球,我们现在向小桶内依次放入2号、1号、3号小球。假如你现在需要拿出2号小球,那就必须先将3号小球拿出,再拿出1号小球,最后才能将2号小球拿出来。在刚才取小球的过程中,我们最先放进去的小球最后才能拿出来,而最后放进去的小球却可以最先拿
msdn中的描述如下:(?= 子表达式)(零宽度正预测先行断言。) 仅当子表达式在此位置的右侧匹配时才继续匹配。例如,w+(?=d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。(?(零宽度正回顾后发断言。) 仅当子表达式在此位置的左侧匹配时才继续匹配。例如,(?此构造不会回溯。msdn描述的比较清楚,如:w+(?=ing) 可以匹配以ing结尾的单词(匹配结果不包括ing),(
1.引入线索二叉树 二叉树的遍历实质上是对一个非线性结构实现线性化的过程,使每一个节点(除第一个和最后一个外)在这些线性序列中有且仅有一个直接前驱和直接后继。但在二叉链表存储结构中,只能找到一个节点的左、右孩子信息,而不能直接得到节点在任一遍历序列中的前驱和后继信息。这些信息只有在遍历的动态过程中才能得到,因此,引入线索二叉树来保存这些从动态过程中得到的信息。 2.建立线索二叉树 为了保
排序与我们日常生活中息息相关,比如,我们要从电话簿中找到某个联系人首先会按照姓氏排序、买火车票会按照出发时间或者时长排序、买东西会按照销量或者好评度排序、查找文件会按照修改时间排序等等。在计算机程序设计中,排序和查找也是最基本的算法,很多其他的算法都是以排序算法为基础,在一般的数据处理或分析中,通常第一步就是进行排序,比如说二分查找,首先要对数据进行排序。在Donald Knuth 的计算机程序设