从尾到头输出单链表
void ReversePrint(SListNode *phead) //phead为链表头指针
{
SListNode *head = phead;
SListNode *tail = head;
if (phead == NULL) //链表为空
{
printf("the list was empty!");
return;
}
if (head->next == NULL) //链表内仅有一个节点
{
printf("%d->NULL", head->data);
}
else//链表内至少2节点
{
ReversePrint(head->next);
printf("%d->", head->data);
}
}
删除一个无头单链表的非尾节点
//删除一个无头单链表的非尾节点
void DelListNode(SListNode **pos) //pos是指向链表头指针的二级指针
{
assert(pos);
SListNode *next = (*pos)->next;
(*pos)->data = next->data;
(*pos)->next = next->next;
free(next);
}
在无头单链表的一个节点前插入一个节点(不能遍历链表)
void PlistInsert(SListNode **pos, DataType x)
{
assert(pos);
if ((*pos)->next == NULL)
{
SListNode *newnode = BuySListNode((*pos)->data);
(*pos)->next = newnode;
(*pos)->data = x;
}
else
{
SListNode *next = (*pos)->next;
SListNode *newnode = BuySListNode((*pos)->data);
newnode->next = next;
(*pos)->next = newnode;
(*pos)->data = x;
}
}
单链表实现约瑟夫环(JosephCircle)
void Joseph(SListNode **fflag,int x) //链表已构成环
{
SListNode *flag = (*fflag);
SListNode *next = (*fflag);
if (flag->next ==flag) //链表内部只有一个节点
{
printf("%d", flag->data);
return;
}
while (flag->next != flag) //链表内至少2个节点
{
SListNode *del =NULL;
int num = x;
for (int i = 1;i < x-1;i++)
{
flag = flag->next;
}
del = flag->next;
next = flag->next->next;
printf("%d ", del->data);
flag->next = next;
free(del);
flag = next;
}
printf("%d", flag->data);
}
逆置/反转单链表
SListNode *reverse_list(SListNode **pphead)
{
assert(pphead);
if ((*pphead)->next == NULL) //链表只有一个节点
{
return *pphead;
}
//链表至少有2个节点
SListNode *p1 = *pphead;
SListNode *p2 = (*pphead)->next;
SListNode *p3 = (*pphead)->next->next;
while (p3)
{
p2->next = p1;
p1 = p2;
p2 = p3;
p3 = p3->next;
}
p2->next = p1;
(*pphead)->next = NULL;
return p2;
}
单链表排序(冒泡排序&快速排序)
void bubble_sort(SListNode **pphead) //冒泡排序
{
assert(pphead);
if ((*pphead)->next == NULL) //链表只有一个节点
{
return;
}
SListNode *phead = *pphead;
int i = 0;
while (phead->next) //计算链表结点数
{
phead = phead->next;
i++;
}
for (;i > 0;i--)
{
int flag = 1; //假设链表已经有序
for (phead = *pphead;phead->next != NULL;phead = phead->next)
{
if (phead->data > (phead->next)->data)
{
int tmp = phead->data;
phead->data = (phead->next)->data;
(phead->next)->data = tmp;
flag = 0;
}
}
if (flag == 1)//判断是否交换过
{
return;
}
}
}
合并两个有序链表,合并后要求链表仍然有序
SListNode *merge_list(SListNode *phead, SListNode *phead1)
{
SListNode *newhead = NULL; //定义两个指针,一个为新链表的头,一个为新链表的尾
SListNode *tail = NULL;
if (phead == NULL) //判断是否有一个链表为空
{
return phead1;
}
if (phead1 == NULL)
{
return phead;
}
if (phead->data < phead1->data) //先给新链表一个头节点
{
newhead = phead;
phead = phead->next;
}
else
{
newhead = phead1;
phead1 = phead1->next;
}
tail = newhead;
while (phead&&phead1) //当两个链表都不为空时,对比元素,取小的链在tail后面
{
if (phead->data < phead1->data)
{
tail->next = phead;
phead = phead->next;
}
else
{
tail->next = phead1;
phead1 = phead1->next;
}
tail = tail->next;
}
if (phead) //如果某一个链表没有链完就补在tail后面
{
tail->next = phead;
}
if (phead1)
{
tail->next = phead1;
}
return newhead;
}
查找链表的中间节点,要求只能遍历一次链表
SListNode *search_mid_node(SListNode *pphead)
{
if (pphead == NULL||(pphead)->next==NULL) //当链表为空或者只有一个节点
{
return pphead;
}
//链表至少有2个节点
SListNode *slow = pphead;
SListNode *fast = pphead;
while (fast&&fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
查找单链表的倒数第k个节点,要求只能遍历一次链表
SListNode *search_last_k(SListNode *phead, int k)
{
assert(phead);
SListNode *tail = phead;
while (--k) //确保链表中至少有k个节点
{
if (tail->next == NULL)
{
printf("error\n");
return NULL;
}
tail = tail->next;
}
while (tail->next) //头尾指针同时向后移动
{
tail = tail->next;
phead = phead->next;
}
return phead;
}
删除单链表的倒数第k个节点。
SListNode *delete_last_k(SListNode **pphead,int k)
{
SListNode *phead = *pphead;
SListNode *p = phead;
SListNode *pos= search_last_k(phead, k); //利用search_last_k找到倒数第k个节点
SListNode *next = NULL;
assert(pos); //确保pos不为空
//考虑pos是否是首节点(头删)或尾节点(尾删)
if (pos == phead)
{
phead = phead->next;
free(pos);
return phead;
}
else //一般情况,包含尾删
{
while (p->next != pos)
{
p = p->next; //找到pos前一个节点
}
next = pos->next; //pos后面的节点(可以是空)
p->next = next;
free(pos);
return phead;
}
}
判断单链表是否带环?若带环,求环的长度?求环的入口点?并计算每个算法的时间复杂度与空间复杂度
SListNode *check_circle(SListNode *phead) //是否带环?
{
assert(phead);
SListNode *fast = phead;
SListNode *slow = phead;
while (fast)
{ //定义快慢指针,快的每次走2个节点,慢的走1个节点
slow = slow->next;
fast = fast->next;
if (fast)
{
fast = fast->next;
}
if (fast == slow) //相等说明快慢指针在环内相遇
{
printf("该链表带环\n");
return slow;
}
}
printf("该链表不带环\n"); //说明该链表有尾
return NULL;
}
int circle_lenth(SListNode *meet) //计算环的长度 参数是环内快慢指针的相遇点
{
SListNode *flag = meet;
int i = 0;
do
{
flag = flag->next;
i++;
}
while (flag != meet);
return i;
}
计算环的入口点相对较难,代码比较简单,先要理清思路(画的很挫…将就一下)
SListNode *search_entrance(SListNode *phead, SListNode *meet)
{
while (phead != meet)
{
phead = phead->next;
meet = meet->next;
}
return meet;
}
判断两个链表是否相交,若相交,求交点(假设链表不带环)
这个问题要与下面的带环问题放在一起讨论,如图:
不带环的只有1,2两种情况,交与不交,我们可以知道如果相交,那么这两个链表的最后一个节点的地址一定相等(不然就交不上了)。这就可以作为我们判断不带环类型是否相交的依据。
而如果确认相交,分别算出两个链表的长度,求出差值x。让长的那个先走两个链表长度的差值的长度。再让两个链表同时走,相等即是交点。
int check_cross(SListNode *phead1, SListNode *phead2)
{
if (phead1 == NULL || phead2 == NULL) //某链表为空一定不相交
{
printf("不相交\n");
return 0;
}
while (phead1) //求2链表最后位置
{
phead1 = phead1->next;
}
while (phead2)
{
phead2 = phead2->next;
}
if (phead1 == phead2) //若相交,最后节点地址一定相等,反之则不等
{
printf("相交\n");
return 1;
}
else
{
printf("不相交\n");
return 0;
}
}
SListNode *find_focus_point(SListNode *phead1, SListNode *phead2)//在已经确认相交的基础上
{
int i = 0;
int j = 0;
SListNode *tail1 = phead1;
SListNode *tail2 = phead2;
while (tail1)
{
tail1 = tail1->next;
}
while (tail2)
{
tail2 =tail2->next;
}
int num = abs(tail1 - tail2);
if (tail1 > tail2)
{
while (num--)
{
phead1 = phead1->next;
}
}
else
{
while (num--)
{
phead2 = phead2->next;
}
}
while (phead1 != phead2)
{
phead1 = phead1->next;
phead2 = phead2->next;
}
return phead1;
判断两个链表是否相交,若相交,求交点(假设链表带环)
还是这个图,如果带环,分有1环和两环2种情况。如果只有1环(即图3);如果是2环(图456),有不交(4)和相交(56)两种情况。现在依次对这几种情况分析:
图3(即一环一不带环)一定是不相交的,如果相交一定变为2环
判断图4可以让meet1不动,meet2继续遍历,如果再次到meet2前仍没有与meet1相遇,则说明两个相遇点不在一环内,即图4,反之则为图5,图6.
图5和图6的区别是一个是环外相交,一个是环内相交。辨别思路如下:
分别让phead1和meet1,phead2和meet2对应,用求环入口点的方法(上面有提及)分别求出入口点,如果两个入口点地址相等,这说明是环外相交(图5)。反之则是环内相交(图6)。
——————————————————————————————————————————————
复杂链表的复制。一个链表的每个节点,有一个指向next指针指向 下一个节点,还有一个random指针指向这个链表中的一个随机节点 或者NULL,现在要求实现复制这个链表,返回复制后的新链表。
思路:
第一步:仍然是根据原始链表的每个结点N 创建对应的 N’。把 N’链接在N的后面。
第二步:设置复制出来的结点的 sibling。假设原始链表上的 N 的 sibling 指向结点 S,那么其对应复制出来的 N’是 N的 pext 指向的结点,同样 S’也是 S 的 next 指向的结点。
把这个长链表拆分成两个链表。把奇数位置的结点用 next 链接起来就是原始链表,把偶数位置的结点用 next 链接起来就是复制 出来的链表。
typedef struct Node
{
int value ;
struct Node *next ;
struct Node *random ;
}Node;
Node *deepCopy (Node *head)
{
Node *p = head, *q = head->next;
//step 1
{
Node *newNode = (Node *)malloc(sizeof(Node));
newNode->next = p->next;
p->next = newNode;
newNode->value = p->value;
newNode->random = NULL;
p = q;
q = q->next;
}
//step 2
p = head;
q = p->next;
while (q != NULL)
{
if (p->random != NULL)
q->random = q->random->next;
if (q->next == NULL)
break;
p = q->next;
q = p->next;
}
//step 3
newNode = head->next;
p = head; q = p->next;
while (q != NULL)
{
p->next = q->next;
if (q->next == NULL)
break;
q->next = p->next->next;
p = p->next;
q = q->next;
}
return newNode;
}
———————————————————————————————————————————————
求两个已排序单链表中相同的数据。
思路:
比较两个指针所指数据的大小,小的向后移,大的不动。遇到相等的,开辟一个新节点存储数据。直到一链表到尾,将开辟的节点链成链表即可。
SListNode *check_common_node(SListNode *phead, SListNode *phead1)
{
SListNode *newhead = NULL;
SListNode *tail = newhead;
if (phead == NULL || phead1 == NULL)//任意一个链表为空返回空
{
return NULL;
}
while (phead&&phead1)
{
if (phead->data < phead1->data) //小的向后走
{
phead = phead->next;
}
if (phead->data > phead1->data)
{
phead1 = phead1->next;
}
else //相等时开辟节点,链在tail后
{
SListNode *newnode = BuySListNode(phead->data);
if (newhead == NULL)
{
newhead = newnode;
tail = newnode;
}
else
{
tail->next = newnode;
tail = newnode;
}
phead = phead->next;
phead1 = phead1->next;
}
}
return newhead;
}