文章目录
顺序表算法题
不熟悉顺序表的可以先了解一下
顺序表实现方法
移除元素
给你一个数组
nums
和一个值val
,你需要 原地 移除所有数值等于val
的元素。元素的顺序可能发生改变。然后返回nums
中与val
不同的元素的数量。假设
nums
中不等于val
的元素数量为k
,要通过此题,您需要执行以下操作:
- 更改
nums
数组,使nums
的前k
个元素包含不等于val
的元素。nums
的其余元素和nums
的大小并不重要。- 返回
k
。
很容易想到的是用双指针进行遍历和赋值即可
c
int removeElement(int* nums, int numsSize, int val) {
int left = 0;
for (int right = 0; right < numsSize; right++) {
if (nums[right] != val) {
nums[left] = nums[right];
left++;
}
}
return left;
}
-
注意题目中说的是元素的顺序可以发生改变,即不需要保持元素的相对顺序,可以进行优化
-
如果左指针 left 指向的元素等于 val,此时将右指针 right 指向的元素复制到左指针 left 的位置,然后右指针 right 左移一位。如果赋值过来的元素恰好也等于 val,可以继续把右指针 right 指向的元素的值赋值过来(左指针 left 指向的等于 val 的元素的位置继续被覆盖),直到左指针指向的元素的值不等于 val 为止。
注意
while
结束条件不要少了等于
-
c
int removeElement(int* nums, int numsSize, int val) {
int left = 0, right = numsSize-1;
while (left <= right) {
if (nums[left] == val) {
nums[left] = nums[right];
right--;
} else {
left++;
}
}
return left;
}
注意:返回的是元素的个数
删除有序数组中的重复项
给你一个 非严格递增排列 的数组
nums
,请你**原地** 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回nums
中唯一元素的个数。考虑
nums
的唯一元素的数量为k
,你需要做以下事情确保你的题解可以被通过:
- 更改数组
nums
,使nums
的前k
个元素包含唯一元素,并按照它们最初在nums
中出现的顺序排列。nums
的其余元素与nums
的大小不重要。- 返回
k
- 和上一道题思路大致相同,题目说了是非严格递增序列,双指针法比较前后两个元素即可
c
int removeDuplicates(int* nums, int numsSize) {
int src = 0;
int dest = 1;
while (dest < numsSize) {
if(nums[src]!=nums[dest])
{
nums[++src]=nums[dest];
}
dest++;
}
return ++src;
}
合并两个有序数组
给你两个按 非递减顺序 排列的整数数组
nums1
和nums2
,另有两个整数m
和n
,分别表示nums1
和nums2
中的元素数目。请你 合并
nums2
到nums1
中,使合并后的数组同样按 非递减顺序 排列。**注意:**最终,合并后数组不应由函数返回,而是存储在数组
nums1
中。为了应对这种情况,nums1
的初始长度为m + n
,其中前m
个元素表示应合并的元素,后n
个元素为0
,应忽略。nums2
的长度为n
。
- 当然可以直接把第二个数组移到第一个数组尾部,然后进行排序
- 使用qsort函数
c
int cmp(int* a, int* b) {
return *a - *b;
}
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
for (int i = 0; i != n; ++i) {
nums1[m + i] = nums2[i];
}
qsort(nums1, nums1Size, sizeof(int), cmp);
}
- 三指针法,利用两个数组都是非递减排列
- 因为排序好的数组仍然是非递减序列,所以我们的两个指针依次从尾部开始向前遍历,谁大把谁放到nums1的尾部 (若从前面开始需要新创建一个数组来存储元素最后再赋值给nums1)
- 最后出循环的时候l2和l3只可能有一个小于0
- 若是l2,说明nums2没有遍历完,需要将剩下的元素赋值给nums1
- 若是l3,则直接返回nums1即可
c
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
int l1=nums1Size-1;
int l2=m-1;
int l3=n-1;
while(l2>=0&&l3>=0)
{
if(nums1[l2]>nums2[l3])
{
nums1[l1]=nums1[l2];
l2--;
}
else
{
nums1[l1]=nums2[l3];
l3--;
}
l1--;
}
if(l2<0)
{
while(l1>=0)
nums1[l1--]=nums2[l3--];
}
}
链表算法题
不熟悉链表的可以先了解一下
单链表实现方法
移除链表元素
给你一个链表的头节点
head
和一个整数val
,请你删除链表中所有满足Node.val == val
的节点,并返回 新的头节点 。
- 最直观的办法就是创建一个新链表
注:不是开辟空间的深拷贝,而只是定义了指向同一结点的指针
在最后需要先判断newtail是否为空**(否则链表为空链表时会报错)再将其中的next指针置为空(否则可能会出现循环)**
c
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
ListNode* removeElements(ListNode* head, int val) {
ListNode* pcur = head;
ListNode* newhead = NULL;
ListNode* newtail = NULL;
while (pcur) {
if (pcur->val != val) {
if (newhead == NULL) {
newhead = newtail = pcur;
} else {
newtail->next = pcur;
newtail = newtail->next;
}
}
pcur = pcur->next;
}
if (newtail)
newtail->next = NULL;
return newhead;
}
- 在leetcode上给出了递归的方法,链表的定义具有递归的性质,因此链表题目常可以用递归的方法求解。这道题要求删除链表中所有节点值等于特定值的节点,可以用递归实现。
c
struct ListNode* removeElements(struct ListNode* head, int val) {
if (head == NULL) {
return head;
}
head->next = removeElements(head->next, val);
return head->val == val ? head->next : head;
}
反转链表
给你单链表的头节点
head
,请你反转链表,并返回反转后的链表。
- 可以和上一道题一样创建新链表,不多赘述
- 这里介绍一种比较巧妙的方法->三指针法
c
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {
if(head==NULL)
return head;
ListNode* n1,*n2,*n3;
n1=NULL,n2=head,n3=head->next;
while(n3){
n2->next=n1;
n1=n2;
n2=n3;
n3=n3->next;
}
n2->next=n1;
return n2;
}
链表的中间结点
给你单链表的头结点
head
,请你找出并返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点
- 快慢指针法
- 注意为偶数时返回第二个结点
c
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) {
ListNode* slow, *fast;
slow=fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
}
return slow;
}
合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
- 和合并数组大致思路相同
- 可以在创建新链表的一开始申请头结点(哨兵位),避免对于newtail和newhead为空的情况进行讨论
- 记得最后释放空间!!!
c
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
ListNode* newhead,*newtail;
newhead=newtail=(ListNode*)malloc(sizeof(ListNode));
while(list1&&list2)
{
if(list1->val<list2->val)
{
newtail->next=list1;
list1=list1->next;
}
else
{
newtail->next=list2;
list2=list2->next;
}
newtail=newtail->next;
}
if(list1)
{
newtail->next=list1;
}
else
{
newtail->next=list2;
}
ListNode* ret=newhead->next;
free(newhead);
newhead=newtail=NUll;
return ret;
}
链表分割
有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针
- 解题思路:创建两个链表,分别存放小于x的节点和大于等于x的节点,分别进行尾插
- 最后别忘了把大链表的尾节点置为空,否则可能会出现死循环
- 还是别忘了释放空间
c
ListNode* partition(ListNode* pHead, int x) {
if(pHead == NULL)
return NULL;
struct ListNode* lessHead, *lessTail,*greaterHead, *greaterTail;
//创建链表表头
lessHead = lessTail = (struct ListNode*)malloc(sizeof(struct ListNode));
greaterHead = greaterTail = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* cur = pHead;
while(cur)
{
//小于x的尾插到lessTail
if(cur->val < x)
{
lessTail->next = cur;
lessTail = lessTail->next;
}
//大于等于x的尾插到greaterTail
else
{
greaterTail->next = cur;
greaterTail = greaterTail->next;
}
cur = cur->next;
}
//链接两个链表
lessTail->next = greaterHead->next;
greaterTail->next = NULL;
//获取表头
pHead = lessHead->next;
free(lessHead);
free(greaterHead);
return pHead;
}
链表的回文结构
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900
- 不推荐的解法,类似数组字符串的回文解法
- 先把链表中的元素值全部保存到数组中,然后再判断数组是否为回文。不建议使用这种解法,因为如果没有告诉链表最大长度,则不能同此解法
c
bool chkPalindrome(ListNode* A) {
// write code here
int a[900] = {0};
ListNode* cur = A;
int n = 0;
//保存链表元素
while(cur)
{
a[n++] = cur->val;
cur = cur->next;
}
//判断数组是否为回文结构
int begin = 0, end = n-1;
while(begin < end)
{
if(a[begin] != a[end])
return false;
++begin;
--end;
}
return true;
}
- 解题思路:此题可以先找到中间节点,然后把后半部分逆置,最近前后两部分一一比对,如果节点的值全部相同,则即为回文。
- 快慢指针找中间结点
- 三指针逆置后半部分
c
bool chkPalindrome(ListNode* A) {
if (A == NULL || A->next == NULL)
return true;
ListNode* slow, *fast, *prev, *cur, *nxt;
slow = fast = A;
//找到中间节点
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
prev = NULL;
//后半部分逆置
cur = slow;
while (cur)
{
nxt = cur->next;
cur->next = prev;
prev = cur;
cur = nxt;
}
//逐点比对
while (A && prev)
{
if (A->val != prev->val)
return false;
A = A->next;
prev = prev->next;
}
return true;
}