/*--------------------CSS部分-------------------*/ /*--------------------JS部分-------------------*/

单链表

前言

顺序表是用一组地址连续的存储单元来保存数据的,所以它具有随机存取的特点。即查找快速,但是做插入或删除动作是,需要移动大量元素,效率较低。
链表是线性表的链式存储结构,它相比于顺序表,在插入和删除元素时,效率要高很多。
每个数据单元有两部分组成,一个是数据域,存储数据值;另一个是指针域,指向下一个数据单元。这样的数据单元叫做结点。
链表可分为单链表、双链表、循环链表。

基本算法

  • 创建链表
    顺序表存储结构的创建就是数组的初始化,即声明一个类型和大小的数组并赋值。单链表是一种松散的存储结构,动态的,同时指针域,必须为指针域赋值。创建链表的过程就是动态生成链表的过程。从空表开始,依次建立各个元素结点,逐个插入链表。
    头插法:采用插队的方式,始终让新建结点在第一的位置上。类似堆栈。
    这里写图片描述
    假设头指针后有数据,往里插入结点即可。
 1 LNode* pTempHead = *ppHead; 
 2 pTempHead->pNext = NULL;
 3 for (int i= 0;i<num;i++)
 4 {
 5     LNode* item = new LNode();//生成新的结点
 6     item->data = i;
 7     //这是头插法,就是在链表顶端往下插入
 8     item->pNext = pTempHead->pNext;
 9     pTempHead->pNext = item;//插入到表头,新插入的结点在表头   
10 
11 }
头插法

 

数组数据显示是:4,3,2,1,0
尾插法:每次新的结点都插在终端结点的后面。

 1 LNode* pTempHead = *ppHead;
 2     //建立一个带头结点的单链表
 3     for (int i= 0;i<num;i++)
 4     {
 5         LNode* item = new LNode();//生成新的结点
 6         item->data = i;     
 7         pTempHead->pNext = item;//将表尾终端结点的指针指向新的结点,此时pphead中添加了结点,item在最后端
 8         pTempHead = item;//继续让pTempHead指向ppHead的终端。
 9 
10     }   
11     pTempHead->pNext = NULL;//尾插法是在这设置为null
尾插法

 

链表数据显示:0,1,2,3,4.
- 插入结点
核心算法就是如图中
这里写图片描述
现将p的后继结点保存,再赋值给s的后继结点,就成了s->Next = p->Next 。此时将s添加到链表中:p->Next = s; 单链表的表头和表尾的特殊情况,操作时相同的。

 1 //插入元素
 2 void SingleList::InsertElem(LNode** ppHead,int pos,int data)
 3 {
 4     pLinkList pTempHead = *ppHead;//指向需要操作的数据链
 5     if (pTempHead == NULL || pTempHead->pNext == NULL)
 6     {
 7         return;
 8     }
 9     pLinkList item;
10     int i = 0;
11     //选找到制定位置的结点,有效的可以在表尾和表头插入结点
12     while(pTempHead && i< pos)
13     {
14         pTempHead = pTempHead->pNext;
15         i++;
16     }
17     //位置不合适。有可能到了表尾此处不能为pTempHead->pNext,当为表尾是next没有分配内存,导致溢出。
18     if (!pTempHead ||i>pos)
19     {
20         return;
21     }
22     //进行插入操作
23     item = new LNode();
24     if (!item)
25     {
26         return;
27     }
28     item->data = data;
29     item->pNext = pTempHead->pNext;
30     pTempHead->pNext = item;
31 
32 }
插入元素

 

  • 删除结点
    删除结点就是将他的前级结点的指针绕过即可。实际上就一步:p->next = p->next-next;同时释放点指针。
    这里写图片描述
 1 //删除元素
 2 void SingleList::RemoveElem(LNode** ppHead,int pos,int* getData)
 3 {
 4     pLinkList pTempHead = *ppHead;//指向需要操作的数据链
 5     if (pTempHead == NULL || pTempHead->pNext == NULL)
 6     {
 7         return;
 8     }
 9     pLinkList item;
10     int i = 0;
11     //选找到制定位置的结点,此处是pTempHead->pNext,有效的能删除表尾,防止访问到表尾的next
12     while(pTempHead->pNext && i< pos)
13     {
14         pTempHead = pTempHead->pNext;
15         i++;
16     }
17     //位置不合适。有可能到了表尾,如果和插入时为ptempHead,此时item的item->next会报错
18     if (!pTempHead->pNext ||i>pos)
19     {
20         return;
21     }
22     item = pTempHead->pNext;
23     *getData = item->data;
24     //item->pNext = pTempHead->pNext;
25     pTempHead->pNext = item->pNext;
26     //释放资源
27     delete item;
28 }
删除结点

 

在删除结点和插入结点时,注意循环跳出的条件,两者不一样。

代码实例

返回类型函数名称参数功能
void InitNode (LNode** ppHead) 初始化结构体
void CreateList (LNode** ppHead,int num) 创建链表
void DestroyList (LNode** ppHead) 清空链表
void InsertElem (LNode** ppHead,int pos,int data) 插入元素
void RemoveElem (LNode** ppHead,int pos,int* getData) 移除元素
bool IsEmptyList (LNode* pHead) 是否为空
int GetElem (LNode* pHead,int pos,int* data) 获取制定位置元素
void PrintList (LNode* pHead) 输出链表
void ReverseList (LNode* pHead,LNode ** outList) 反转链表
  1 void SingleList::InitNode(LNode** ppHead)
  2 {
  3     if (*ppHead)
  4     {
  5         DestroyList(ppHead);
  6     }
  7     LNode* p = new LNode();
  8     p->data = 0;
  9     p->pNext = NULL;
 10     *ppHead = p;
 11 }
 12 //创建链表
 13 void SingleList::CreateList(LNode** ppHead,int num)
 14 {
 15     InitNode(ppHead);
 16     LNode* pTempHead = *ppHead;
 17     //建立一个带头结点的单链表
 18     //pTempHead = new LNode();
 19     //pTempHead->pNext = NULL;
 20     for (int i= 0;i<num;i++)
 21     {
 22         LNode* item = new LNode();//生成新的结点
 23         item->data = i;
 24         //这是头插法,就是在链表顶端往下插入
 25         //item->pNext = pTempHead->pNext;
 26         //pTempHead->pNext = item;//插入到表头,新插入的结点在表头
 27         //这是头插法
 28         //item->pNext = pTempHead;//将表尾终端节点的指针指向新的结点,此时item是多个点
 29         //pTempHead = item;//将item赋值给item
 30         pTempHead->pNext = item;
 31         pTempHead = item;
 32 
 33     }
 34 
 35     pTempHead->pNext = NULL;//尾插法是在这设置为null
 36     //*ppHead = pTempHead;
 37 }
 38 //销毁链表
 39 void SingleList::DestroyList(LNode** ppHead)
 40 {
 41     pLinkList pTempHead = *ppHead;
 42     //前期判断,已经为空,返回
 43     if (pTempHead == NULL||pTempHead->pNext == NULL)
 44     {
 45         return;
 46     }
 47     pLinkList item,item1;
 48     item = pTempHead->pNext;
 49     while(item)//没到表尾
 50     {
 51         item1= item->pNext;
 52         delete item;
 53         item = item1;
 54     }
 55     pTempHead->pNext = NULL;
 56 }
 57 //插入元素
 58 void SingleList::InsertElem(LNode** ppHead,int pos,int data)
 59 {
 60     pLinkList pTempHead = *ppHead;//指向需要操作的数据链
 61     if (pTempHead == NULL || pTempHead->pNext == NULL)
 62     {
 63         return;
 64     }
 65     pLinkList item;
 66     int i = 0;
 67     //选找到制定位置的结点
 68     while(pTempHead && i< pos)
 69     {
 70         pTempHead = pTempHead->pNext;
 71         i++;
 72     }
 73     //位置不合适。有可能到了表尾此处不能为pTempHead->pNext,当为表尾是next没有分配内存,导致溢出。
 74     if (!pTempHead ||i>pos)
 75     {
 76         return;
 77     }
 78     //进行插入操作
 79     item = new LNode();
 80     if (!item)
 81     {
 82         return;
 83     }
 84     item->data = data;
 85     item->pNext = pTempHead->pNext;
 86     pTempHead->pNext = item;
 87 
 88 }
 89 //删除元素
 90 void SingleList::RemoveElem(LNode** ppHead,int pos,int* getData)
 91 {
 92     pLinkList pTempHead = *ppHead;//指向需要操作的数据链
 93     if (pTempHead == NULL || pTempHead->pNext == NULL)
 94     {
 95         return;
 96     }
 97     pLinkList item;
 98     int i = 0;
 99     //选找到制定位置的结点,此处是pTempHead->pNext,有效的能删除表尾,防止访问到表尾的next
100     while(pTempHead->pNext && i< pos)
101     {
102         pTempHead = pTempHead->pNext;
103         i++;
104     }
105     //位置不合适。有可能到了表尾,如果和插入时为ptempHead,此时item的item->next会报错
106     if (!pTempHead->pNext ||i>pos)
107     {
108         return;
109     }
110     item = pTempHead->pNext;
111     *getData = item->data;
112     //item->pNext = pTempHead->pNext;
113     pTempHead->pNext = item->pNext;
114     //释放资源
115     delete item;
116 }
117 //判断单链表是否为空
118 bool SingleList::IsEmptyList(LNode* pHead)
119 {
120     if (NULL == pHead||NULL == pHead->pNext)
121     {
122         return true;
123     }
124     else
125         return false;
126 }
127 //获取单链表位置为pos的元素,从头开始找,直到第i元素为止,时间复杂度为N。
128 int SingleList::GetElem(LNode* pHead,int pos,int* data)
129 {
130     int j = 1;//计数器
131     if (pHead == NULL||pos<1)
132     {
133         return -1;
134     }
135     pLinkList item;//声明一个结点
136     item = pHead->pNext;//指向头结点
137     while(item && j<pos)
138     {
139         item =pHead->pNext;
140         j++;
141     }
142     *data = item->data;
143     return 1;
144 }
145 
146 #pragma endregion
147 
148 #pragma region 链表扩展操作
149 //从尾到头打印链表
150 void SingleList::PrintList(LNode* pHead)
151 {
152     //此处是反转打印链表,可以利用堆栈
153     stack<LNode*> stackList ;
154     pLinkList item = pHead;
155     while(item != NULL)
156     {
157         //压入堆栈
158         stackList.push(item);
159         item = item->pNext;
160     }
161     while(!stackList.empty())
162     {
163         //导出数据
164         item = stackList.top();
165         cout<<item->data;
166         stackList.pop();
167     }
168 }
169 
170 void SingleList::PrintList1(LNode* pHead)
171 {
172     if (NULL == pHead || NULL == pHead->pNext) {
173 
174         printf("LinkList is empty\n");
175 
176         return;
177 
178     }
179 
180     LNode *p = pHead->pNext;
181 
182     printf("LinkList:");
183 
184     while (p) {
185 
186         printf(" %d", p->data);
187 
188         p = p->pNext;
189 
190     }
191 
192     printf("\n");
193 }
194 //反转链表
195 void SingleList::ReverseList(LNode* pHead,LNode ** outList)
196 {
197     if (pHead == NULL||pHead->pNext == NULL)
198     {
199         return;
200     }
201     pLinkList pTempHead = *outList;
202     pTempHead = new LNode();
203     pLinkList pCurrent = pHead;
204     while(pCurrent)
205     {
206         LNode * item = pCurrent;
207         //采用尾插法,创建链表即可;
208         item->pNext = pTempHead;
209         pTempHead = item;
210         pCurrent = pCurrent->pNext;
211     }   
212 
213 }
代码实例

 

面试题

查找单链表中的倒数第K个结点(k > 0)
思路:使用两个指针,先让前面的指针走到正向第k个结点,这样前后两个指针的距离差是k-1,之后前后两个指针一起向前走,前面的指针走到最后一个结点时,后面指针所指结点就是倒数第k个结点。

// 查找单链表中倒数第K个结点
 1 LNode * RGetKthNode(LNode * pHead, unsigned int k) // 函数名前面的R代表反向
 2 {
 3     if(k == 0 || pHead == NULL) // 这里k的计数是从1开始的,若k为0或链表为空返回NULL
 4         return NULL;
 5 
 6     LNode * pAhead = pHead;
 7     LNode * pBehind = pHead;
 8     while(k > 1 && pAhead != NULL) // 前面的指针先走到正向第k个结点
 9     {
10         pAhead = pAhead->m_pNext;
11         k--;
12     }
13     if(k > 1 || pAhead == NULL)     // 结点个数小于k,返回NULL
14         return NULL;
15     while(pAhead->m_pNext != NULL)  // 前后两个指针一起向前走,直到前面的指针指向最后一个结点
16     {
17         pBehind = pBehind->m_pNext;
18         pAhead = pAhead->m_pNext;
19     }
20     return pBehind;  // 后面的指针所指结点就是倒数第k个结点
21 }
View Code

 

 

查找单链表的中间结点
思路:设置两个指针,只不过这里是,两个指针同时向前走,前面的指针每次走两步,后面的指针每次走一步,前面的指针走到最后一个结点时,后面的指针所指结点就是中间结点。

 1 这里写代码片// 获取单链表中间结点,若链表长度为n(n>0),则返回第n/2+1个结点
 2 ListNode * GetMiddleNode(LNode * pHead)
 3 {
 4     if(pHead == NULL || pHead->m_pNext == NULL) // 链表为空或只有一个结点,返回头指针
 5         return pHead;
 6 
 7     L * pAhead = pHead;
 8     L * pBehind = pHead;
 9     while(pAhead->m_pNext != NULL) // 前面指针每次走两步,直到指向最后一个结点,后面指针每次走一步
10     {
11         pAhead = pAhead->m_pNext;
12         pBehind = pBehind->m_pNext;
13         //防止表尾,内存溢出.走了两步
14         if(pAhead->m_pNext != NULL)
15             pAhead = pAhead->m_pNext;
16     }
17     return pBehind; // 后面的指针所指结点即为中间结点
18 }
View Code

 

已知两个单链表pHead1 和pHead2 各自有序,把它们合并成一个链表依然有序

 1 // 合并两个有序链表
 2 ListNode * MergeSortedList(ListNode * pHead1, ListNode * pHead2)
 3 {
 4     //如果head1为空,返回head2
 5     if(pHead1 == NULL)
 6         return pHead2;
 7     //如果head2为空,返回head1,这些处理防止后面访问链表中元素时,内存溢出
 8     if(pHead2 == NULL)
 9         return pHead1;
10     ListNode * pHeadMerged = NULL;
11     //比较大小,确定合并链表的表头指向谁。
12     if(pHead1->m_nKey < pHead2->m_nKey)
13     {
14         pHeadMerged = pHead1;
15         pHeadMerged->m_pNext = NULL;
16         pHead1 = pHead1->m_pNext;
17     }
18     else
19     {
20         pHeadMerged = pHead2;
21         pHeadMerged->m_pNext = NULL;
22         pHead2 = pHead2->m_pNext;
23     }
24     ListNode * pTemp = pHeadMerged;
25     //开始向合并链表中添加节点
26     while(pHead1 != NULL && pHead2 != NULL)
27     {
28         if(pHead1->m_nKey < pHead2->m_nKey)
29         {
30             //head1中元素小,通过尾插法,将小的结点先放在链表尾。
31             //将head1插入链表中
32             pTemp->m_pNext = pHead1;
33             //head1下移,继续循环
34             pHead1 = pHead1->m_pNext;
35             //让temp始终指向表尾,也是就next
36             pTemp = pTemp->m_pNext;
37             //表尾设置为null.
38             pTemp->m_pNext = NULL;
39         }
40         else
41         {
42             pTemp->m_pNext = pHead2;
43             pHead2 = pHead2->m_pNext;
44             pTemp = pTemp->m_pNext;
45             pTemp->m_pNext = NULL;
46         }
47     }
48     if(pHead1 != NULL)
49         pTemp->m_pNext = pHead1;
50     else if(pHead2 != NULL)
51         pTemp->m_pNext = pHead2;
52     return pHeadMerged;
53 }
View Code

 

版权声明:本文为博主原创文章,未经博主允许不得转载。

posted @ 2015-08-05 15:37  bldong  阅读(813)  评论(0编辑  收藏  举报