首先链表节点的定义如下:
typedef struct node { int data; struct node *next; }Node;
面试题1:创建一个单链表
功能函数:
/****创建带有头结点的单链表******/ Node *creat(int n) { Node *head,*p,*q; int x; head=(Node *)malloc(sizeof(Node)); /*创建头结点*/ q=head; if(n>0) { printf("Please input the data of the node:"); while(n>0) { scanf("%d",&x); p=(Node *)malloc(sizeof(Node)); p->data=x; q->next=p; q=p; n--; } } q->next=NULL; return head; }
面试题2:编程实现单链表的打印
功能函数:
/****打印单链表******/ void print(Node *head) { Node *p; if(head->next==NULL) { printf("The LinkList is Empty ! "); return; } p=head->next; while(p!=NULL) { printf("%d ",p->data); p=p->next; } }
面试题3:编程实现从尾到头打印单链表(剑指offer)
思路1:第一反应是将链表中的指针反转,改变链表的方向,然后再从头打印出来(该方法改变了原来链表的结构,这就要看面试或笔试时的要求,可否改变链表结构)
思路2:遍历链表时是从头到尾,而打印链表时却是从尾到头,典型的“先进后出”——栈的特点,从而可以用栈来实现。没遍历一个节点,把该节点放到一个栈中,当遍历完整个链表后,再从栈顶开始逐个输出节点的值,此时输出的节点顺序已经反转过来了。
思路3:栈可以实现,而递归本质上也是一个栈结构,从而可以用递归来实现。即:没遍历到一个节点时,先递归输出其后面的一个节点的值,再输出该节点本身的值。
功能函数(思路3):
void Rev_print(Node *head) { Node *p; if(head==NULL || head->next==NULL) return; else { p=head->next; Rev_print(p); printf("%d ",p->data); } }
面试题4:编程实现查找单链表的中间节点
解题思路:A和B赛跑,其中A的速度是B的速度的两倍,当A到达终点时,B刚好到达中点
功能函数:
Node *find_midNode(Node *head) { Node *p1,*p2; if(head==NULL || head->next==NULL) //链表为空,或是单结点链表直接返回头结点 return head; p1=p2=head->next; while(1) { if(p2->next!=NULL && p2->next->next!=NULL) { p2=p2->next->next; p1=p1->next; } else break; } return p1; }
面试题5:编程实现查找单链表的倒数第k个节点
思路1:首先求出单链表总的长度n,然后从链表的头节点开始遍历,当遍历到n-m个节点时,即为链表中倒数第m个节点
思路2:首先设置两个指针p1和p2,同时指向单链表的头节点,然后用p2遍历链表,将p2指向链表中第m个节点,接着将p1和p2同时进行遍历,当p2指向链表中末尾节点时,p1所指向的节点即为倒数第m个节点。
功能函数(思路1):
Node *Find_Rev_node_1(Node *head,int k) //思路1 { Node *p; int n=0,i; p=head; while(p!=NULL) { n++; p=p->next; } if (n==0 || k>n) { return NULL; } p=head; for(i=0;i<n-k;i++) { p=p->next; } return p; }
功能函数(思路2):
Node *Find_Rev_node_2(Node *head,int k) //思路2 { Node *p,*p1,*p2; int n=0,i; p=head; while(p!=NULL) { n++; p=p->next; } if (n==0 || n<k) { return NULL; } p1=p2=head; for(i=0;i<k;i++) { p2=p2->next; } while(p2) { p2=p2->next; p1=p1->next; } return p1; }
面试题6:编程实现判断单链表是否存在环形链表问题
思路:使用快慢指针,慢指针每次只前进一步,快指针每次前进两步,直到慢指针遇上快指针。分别设置两个指针p1,p2.每循环一次,p2前进两步,p1前进一步,直到p2与p1相等或p2碰到NULL时结束。如果两个指针相等,则说明存在回环。
功能函数:
int Isloop_LinkList(Node *head) { Node *p1,*p2; if(head==NULL || head->next==NULL) //链表为空,或是单结点链表直接返回头结点 return 0; p1 = p2 = head; while(p2->next!=NULL && p2->next->next!=NULL) { p1=p1->next; p2=p2->next->next; if(p1==p2) return 1; } return 0; }
面试题7:编程实现单链表的逆置
思路:遍历一遍链表,利用一个辅助指针保存当前节点的下一个节点,然后将当前节点的指针反转,利用已经存储的指针往后继续遍历
功能函数:
Node *reverse_LinkList(Node *head) { Node *p,*q,*r; if(head==NULL || head->next==NULL) //链表为空,或是单结点链表直接返回头结点 return head; p=head->next; //第一个节点 q=p->next; //保存第二个节点 p->next=NULL; //原第一个节点为末节点 while(q!=NULL) { r=q->next; q->next=p; p=q; q=r; } head->next=p; //新的第一个节点为原末节点 return head; }
测试程序:
头文件:
#include<stdio.h>
#include<stdlib.h>
主函数:
int main() { int len,k; int flag=0; Node *LinkList,*Rev_LinkList; Node *a,*b,*c; printf("Please input the length of the LinkList:"); scanf("%d",&len); LinkList=creat(len); printf("创建的链表如下: "); print(LinkList); printf(" "); printf("从尾到头打印链表如下: "); Rev_print(LinkList); printf(" "); a=find_midNode(LinkList); printf("The middle node of the LinkList is:"); printf("%d ",a->data); printf(" "); printf("思路1:请输入要查找的倒数第几个节点? "); scanf("%d",&k); b=Find_Rev_node_1(LinkList,k); printf("链表的倒数第%d个节点为:",k); printf("%d ",b->data); printf(" "); printf("思路2:请输入要查找的倒数第几个节点? "); scanf("%d",&k); c=Find_Rev_node_2(LinkList,k); printf("链表的倒数第%d个节点为:",k); printf("%d ",c->data); printf(" "); flag=Isloop_LinkList(LinkList); if(flag==1) printf("该链表存在回环! "); else printf("该链表不存在回环! "); printf("链表反转后新的链表如下: "); Rev_LinkList=reverse_LinkList(LinkList); print(Rev_LinkList); printf(" "); system("pause"); return 1; }
测试结果: