单链表的几个基本问题:
1.比较顺序表和链表的优缺点,说说它们分别在什么场景下使用?
顺序表(物理位置相邻):优点:在一段内存中用数组连续存放,所以方便随机查找元素。
缺点:动态开辟,容易造成内存浪费,需要一个元素,开辟过多。前面添加元素时,要逐个挪动后面的每个元素,较麻烦。
场景:数据频繁查找修改,但很少添加。
单链表(物理位置不相邻):优点:方便开辟,用多少开辟多少,不会造成内存空间浪费。
缺点:不方便直接查找。
场景:频繁添加数据,但很少查询。
2.从尾到头打印单链表 :
可以运用递归的方法从尾到头打印。
从头节点向后递归,结束条件当节点为空
返回输出该节点的值。
void PrintTailToHead(ListNode *pList) //逆序打印 用递归
{
if (pList == NULL)
return;
PrintTailToHead(pList->next);//子问题
printf("%d->", pList->data);
}
3.删除一个无头单链表的非尾节点 :
1.将后面节点的值赋给pos
2.删除pos
void EraseNonTail(ListNode *pos) //无头删除指定非尾节点
{
assert(pos);
ListNode *cur = pos->next;
//1.交换与后面的值
DataType tmp = cur->data;
cur->data = pos->data;
pos->data = tmp;
//2.删除后面的节点
pos->next = cur->next;
free(cur);
}
4.在无头单链表的一个节点前插入一个节点:
1.在pos后面插入一节点
2.将pos与后面的值交换
void InsertNonFront(ListNode *pos, DataType x) //在无头单链表的一个节点前插入一个节点
{
assert(pos);
//1.后面插入
ListNode *tmp = BuyNode(x);
tmp->next = pos->next;
pos->next = tmp;
//2.值交换
DataType n = pos->data;
pos->data = tmp->data;
tmp->data = n;
}
5.单链表实现约瑟夫环 :
已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
俩个循环 里循环走数k个,将第k个删除。外循环从删除后下一个再开始直到剩下一个节点。
ListNode* JoseohRing(ListNode *pList, int k)//约瑟夫环
{
if (pList == NULL)
return NULL;
ListNode *cur = pList;
while (cur->next != cur)
{
int count = k;
while (--count) //走k-1次
{
cur = cur->next;
}
ListNode *next = cur->next;
cur->data = next->data;
cur->next = next->next;
free(next);
}
return cur;
}
6.逆置/反转单链表
ListNode* Reverse(ListNode *pList)//链表的逆置
{
if ((pList == NULL) || (pList->next == NULL))
return pList;
else
{
ListNode *newHead = NULL;
ListNode *cur = pList;
while (cur)
{
ListNode *tmp = cur;
cur = cur->next;
tmp->next = newHead;
newHead = tmp;
}
return newHead;
}
}
7.单链表排序(冒泡排序|快速排序)
void BubbleSort(ListNode *pList)//冒泡排序
{
if ((pList == NULL) || (pList->next == NULL))
return;
else
{
ListNode *tail = NULL;
while (tail != pList->next)
{
int exchange = 0;
ListNode *cur = pList;
ListNode *next = cur->next;
while (next != tail)
{
if (cur->data > next->data)
{
DataType tmp = cur->data;
cur->data = next->data;
next->data = tmp;
exchange = 1;
}
next = next->next;
cur = cur->next;
}
if (exchange == 0)
return;
tail = cur;
}
}
}
8.合并两个有序链表,合并后依然有序
ListNode* MergeList(ListNode *pList1, ListNode *pList2)//归并排序
{
if (pList1 == NULL)
return pList2;
else if (pList2 == NULL)
return pList1;
else
{
//摘做头
ListNode *list = NULL;
if (pList2->data < pList1->data)
{
list = pList2;
pList2 = pList2->next;
}
else
{
list = pList1;
pList1 = pList1->next;
}
ListNode *tail = list;
while (pList1 && pList2)
{
if (pList1->data < pList2->data)
{
tail->next = pList1;
pList1 = pList1->next;
}
else
{
tail->next = pList2;
pList2 = pList2->next;
}
tail = tail->next;
}
if (pList1)
tail->next = pList1;
else
tail->next = pList2;
return list;
}
}
9.查找单链表的中间节点,要求只能遍历一次链表
定义 fast 与 slow fast每走2个slow走一个 fast走完全部,slow走了一半 为中节点
ListNode* FindMidNode(ListNode *pList)//查找单链表的中间节点
{
ListNode *fast = pList;
ListNode *slow = pList;
ListNode *prev = pList;
while ((fast)&&(fast->next))
{
fast = fast->next;
fast = fast->next;
prev = slow;
slow = slow->next;
}
if (fast == NULL)
slow = prev;
return slow;
}
10.查找单链表的倒数第k个节点,要求只能遍历一次链表
将fast先走k-1 然后与slow同时走 fast走动尾节点时,slow为倒数第k节点
//k<=链表个数
ListNode* FindTailkNode(ListNode *pList, DataType k)//查找单链表的倒数第k个节点
{
if (pList == NULL)
return NULL;
ListNode *fast = pList;
ListNode *slow = pList;
while (--k)
{
fast = fast->next;
}
while (fast->next)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}