文章目录
序号21
题目:将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
解析:这是无头节点的
1.双指针
先按有头节点做,最后头节点向后移,也可叫做伪头节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
struct ListNode *r, *preHead;
preHead = (struct ListNode*)malloc(sizeof(struct ListNode));
r = preHead;
while (l1 && l2)
{
if (l1->val <= l2->val)
{
r->next = l1;
l1 = l1->next;
}
else
{
r->next = l2;
l2 = l2->next;
}
r = r->next;
}
r->next = l1 ? l1 : l2;
r = preHead->next;
free(preHead);
return r;
}
序号83
题目:给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
解析:
- 模拟
设置双指针,相同就删除,防止断链就行
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* deleteDuplicates(struct ListNode* head){
if (head == NULL)
return head;
struct ListNode *p, *q, *r;
p = head;
q = head->next;
while (q != NULL)
{
if (p->val == q->val)
{
r = q->next;
p->next = r;
free(q);
q = r;
}
else
{
p = q;
q = q->next;
}
}
return head;
}
序号141
题目:给定一个链表,判断链表中是否有环。
解析:
- 快慢指针
快指针每次多走一步,如果有环,就会与慢指针相遇
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool hasCycle(struct ListNode *head) {
if (head == NULL || head->next == NULL)
return false;
struct ListNode *slow, *fast;
slow = head;
fast = head->next;
while (slow != fast)
{
if (fast == NULL ||fast->next == NULL)
return false;
slow = slow->next;
fast = fast->next->next;
}
return true;
}
序号160
题目:编写一个程序,找到两个单链表相交的起始节点。
解析:
- 尾部对齐
较长的链表指针前移到与短链表相同长度的位置,双方同时前进,找到交叉点 - 成环,双指针
若两者不相交,各自走完两端链表都刚好到空
若相交且长度不同,必要走对方的链表。相交的部分都要走一遍,剩下部分的长度叠加,各自要走一遍。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
if (headA == NULL || headB == NULL)
return NULL;
struct ListNode *p = headA, *q = headB;
while (p != q)
{
p = p->next;
q = q->next;
if (p == NULL && q == NULL)
return NULL;
if (p == NULL)
p = headB;
else if (q == NULL)
q = headA;
}
return p;
}
序号203
题目:删除链表中等于给定值 val 的所有节点。
解析:
- 模拟
设置一个伪头节点,模拟删除
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode *pre, *p, *r, *preHead;
preHead = (struct ListNode*)malloc(sizeof(struct ListNode));
preHead->next = head;
pre = preHead;
p = head;
while (p)
{
if (p->val != val)
{
pre = p;
p = p->next;
}
else
{
r = p->next;
free(p);
pre->next = r;
p = r;
}
}
r = preHead->next;
free(preHead);
return r;
}
序号206
题目:反转一个单链表。
解析:
- 头插法
设置一个伪头结点,重新头插,记得 head->next 设置为 NULL,它是最后一个结点 - 模拟
设置 3 个指针,按顺序反转
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
struct ListNode *pre, *p, *t;
pre = NULL;
p = head;
while (p)
{
t = p->next;
p->next = pre;
pre = p;
p = t;
}
return pre;
}
序号234
题目:请判断一个链表是否为回文链表。
解析:
- 数据写入一个数组中
用数组判断回文
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool isPalindrome(struct ListNode* head){
int len = 0, i, flag = 1;
struct ListNode *p = head;
for (; p; p = p->next)
len++;
int* arr = (int*)malloc(sizeof(int)*len);
for (p = head, i = 0; p; p = p->next)
arr[i++] = p->val;
for (i = 0; i < len / 2; i++)
{
if (arr[i] != arr[len-1-i])
{
flag = 0;
break;
}
}
free(arr);
return flag ? true : false;
}
序号237
题目:请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。
解析:
- 与后面结点交换值,删除后面结点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
void deleteNode(struct ListNode* node) {
struct ListNode *p;
node->val = node->next->val;
p = node->next;
node->next = p->next;
free(p);
}
序号876
题目:给定一个带有头结点 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
解析:
- 单指针
先遍历一遍得到长度,第二遍得到 - 快慢指针
快指针走两步,慢指针走一步,快指针到达终点时,慢指针就在中间
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head){
struct ListNode *slow, *fast;
slow = fast = head;
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
序号1290
题目:给你一个单链表的引用结点 head。链表中每个结点的值不是 0 就是 1。已知此链表是一个整数数字的二进制表示形式。
请你返回该链表所表示数字的 十进制值 。
解析:
- 进制转换
和十进制相似,累乘 10 换成累乘 2
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
int getDecimalValue(struct ListNode* head){
int sum = 0;
struct ListNode *p = head;
while (p)
{
sum = sum * 2 + p->val;
p = p->next;
}
return sum;
}
面试 02.01
题目:编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。
提示:
链表长度在[0, 20000]范围内。
链表元素在[0, 20000]范围内。
解析:
- 散列记录是否删除
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeDuplicateNodes(struct ListNode* head){
if (head == NULL || head->next == NULL)
return head;
int *hash = (int*)calloc(20005,sizeof(int));
struct ListNode* pre, *p, *temp;
pre = head; //第一个开始不会重复
hash[head->val] = 1; //第一个先加入
p = head->next;
while (p) //从第二个开始判断
{
if (hash[p->val] != 0)
{
temp = p->next;
free(p);
p = temp;
pre->next = p;
}
else
{
hash[p->val] = 1;
pre = p;
p = p->next;
}
}
free(hash);
return head;
}
面试02.02
题目:实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。
给定的 k 保证是有效的。
解析:
- 双指针
设置间隔为 k 的双指针,第一个指针走到空,第二个便是答案
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
int kthToLast(struct ListNode* head, int k){
struct ListNode *slow, *fast;
slow = fast = head;
while (k--)
fast = fast->next;
while (fast)
{
slow = slow->next;
fast = fast->next;
}
return slow->val;
}
面试02.03
题目:实现一种算法,删除单向链表中间的某个节点(除了第一个和最后一个节点,不一定是中间节点),假定你只能访问该节点。
解析:
- 交换与后面的值,删除后面那个结点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
void deleteNode(struct ListNode* node) {
struct ListNode *p = node->next;
node->val = p->val;
node->next = p->next;
free(p);
}
面试02.06
题目:编写一个函数,检查输入的链表是否是回文的。
解析:
- 记录下链表的元素,用数组判断回文
- 快慢指针找到中间点和结尾点,反转后半部分,然后再回文判断
方法1:
bool isPalindrome(struct ListNode* head){
int* hash = (int*)malloc(sizeof(int) * 10000);
int cnt = 0;
struct ListNode *p = head;
while (p)
{
hash[cnt++] = p->val;
p = p->next;
}
for (int i = 0; i < cnt/2; i++)
{
if (hash[i] != hash[cnt-1-i])
return false;
}
free(hash);
return true;
}
方法2:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool isPalindrome(struct ListNode* head){
if (head == NULL || head->next == NULL)
return true;
struct ListNode *slow, *fast, *p, *q, *mid;
p = slow = head;
fast = head->next;
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
mid = slow;
p = slow->next;
while (p)
{
q = p->next;
p->next = slow;
slow = p;
p = q;
}
p = head;
while (1)
{
if (p != mid)
{
if (p->val != slow->val)
return false;
else
{
p = p->next;
slow = slow->next;
continue;
}
}
if (p == mid && slow == mid)
return true;
if (p == mid && slow->next == mid)
{
if (p->val == slow->val)
return true;
else
return false;
}
}
return true;
}
面试18
题目:给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
解析:
- 模拟删除
设置一个 preHead,统一删除步骤
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* deleteNode(struct ListNode* head, int val){
struct ListNode *pre, *p, *preHead;
preHead = (struct ListNode *)malloc(sizeof(struct ListNode));
preHead->next = head;
pre = preHead;
p = head;
while (p)
{
if (p->val == val)
{
pre->next = p->next;
head = preHead->next;
free(preHead);
return head;
}
pre = p;
p = p->next;
}
return head;
}