一、C语言编程LeetCode数据结构题

文章介绍了链表相关算法,如两数相加、删除倒数第N个节点、合并有序链表、两两交换节点、旋转链表、删除重复元素II、分隔链表、反转链表、相交链表等。此外,还涉及二叉树的中序遍历、验证二叉搜索树、相同的树、对称二叉树、最大深度、平衡二叉树、最小深度、路径总和、根节点到叶节点数字之和、翻转二叉树、左叶子之和等操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、链表

1、两数相加
算法思想:
1、设置两个指针p,q,分别指向两个链表的头结点
2、设置一个临时变量temp,用来记录两个数相加时是否有进位,初始化为0。只要p,q指针不指向空,就循环把两个指针所指向节点的值和temp相加。如果大于9,就让一个临时变量设置为1,并把相加结果减10,把结果赋给两个指针所指向节点的值;如果小于9就直接赋值给两个节点所指向的值。然后p,q指针分别后移一个节点。
3、最后,判断两个链表哪个更长,如果一样长,直接返回其中一个链表。如果不一样长,再判断一次进位,把临时变量加给哪个长的链表所对应节点,返回长的链表的头结点。

struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){
	struct ListNode *p=l1,*q=l2;
	int temp=0;
	while(p!=NULL&&q!=NULL){
		p->val+=temp;
		temp=0;
		p->val=p->val+q->val;
		if(p->val>9){
			p->val-=10;
			temp++;
		}
		q->val=p->val;
		p=p->next;
		q=q->next;
	}
	if(q==NULL&&p==NULL){
		return l1;
	}
	else if(q==NULL){
		p->val+=temp;
		return l1;
	}
	else{
		q->val+=temp;
		return l2;
	}
}

2、删除链表的倒数第 N 个结点
算法思想:
1、使用双指针的思想,设置头指针p和尾指针q,初始时都指针链表头结点。先让q指针后移n个位置。
2、同时向后移动p,q指针,直到q指针到达最后一个结点,这样p指针就指向了倒数第n个结点,同时设置t指针指向p指针前面一个位置,方便删除操作。
3、把t指针指向p指针的下一个指针,这样就完成了删除操作,返回链表节点。


struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
	if(head->next==NULL){
		return NULL;
	}
	struct ListNode *p=head,*q=head,*t=head;
	while(--n){
		q=q->next;
	}
	while(q->next!=NULL){
		q=q->next;
		t=p;
		p=p->next;
	}
	t->next=p->next;
	return head;
}

3、合并两个有序链表
算法思想:
1、新建一个头指针,作为一个新的有序链表的头结点。设置p,q指针分别指向两个链表的头结点。
2、循环移动p,q指针,直到p,q指针有指向空结点才停止。比较p,q指针所指向结点值的大小,把小的结点加到新的链表后面。
3、判断哪个链表还有剩余结点,直接把这些剩余结点都加到新的链表后面,返回新链表的头指针,。

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
	struct ListNode *p=list1,*q=list2,*r;
	struct ListNode *x = (struct ListNode*)malloc(sizeof(struct ListNode));
	r=x;
	x->next=NULL;
	while(p!=NULL&&q!=NULL){
		if(p->val<q->val){
			r->next=p;
			p=p->next;
		}
		else{
			r->next=q;
			q=q->next;
		}
		r=r->next;
	}
	if(p!=NULL){
		r->next=p;
	}
	else{
		r->next=q;
	}
	return x->next;
}

4、两两交换链表中的节点
算法思想:
1、设置一个哑指针指向头结点,方便后续操作,设置一个移动指针p,哑指针指向p
2、循环判断p的下一个结点和下两个结点是否为空,不为空的话,就交换两个结点的位置,并把移动指针指向交换后的后面那个指针。
3、直到p下一个指针或下两个指针为空,说明交换结束,返回头指针,即哑指针的下一个结点。

struct ListNode* swapPairs(struct ListNode* head){
	struct ListNode *r= (struct ListNode*)malloc(sizeof(struct ListNode));
	r->next = head;
	struct ListNode* p = r;
	    while (p->next != NULL && p->next->next != NULL) {
	        struct ListNode* node1 = p->next;
	        struct ListNode* node2 = p->next->next;
	        p->next = node2;
	        node1->next = node2->next;
	        node2->next = node1;
	        p = node1;
	    }
	    return r->next;
}

5、旋转链表
算法思想:
1、使用双指针的思想,设置p,q指针指向头结点。同时遍历一遍链表,得到链表的长度len
2、因为k如果大于len的值,说明整体重复移动了超过一遍,所以可以对k取余,保证整体移动少于一次。把q指针先后移k%len个位置,然后同时移动p,q,当q指针指向最后一个结点时,p指针指向倒数第k个结点
3、记录p指针的下一个指针,因为这是旋转后的头结点,把p指针指向结点和下一个结点断开,同时把尾指针和头指针相连,这样就完成了链表的旋转。

struct ListNode* rotateRight(struct ListNode* head, int k){
	struct ListNode *p=head,*q=head;
    if(p==NULL){
		return head;
	}
	int len=1;
	while(p->next!=NULL){
		p=p->next;
		len++;
	}
	p=head;
	k=k%len;
	while(k-->0){
		q=q->next;
	}
	while(q->next!=NULL){
		q=q->next;
		p=p->next;
	}
	q->next=head;
	q=p->next;
	p->next=NULL;
	return q;
}

6、 删除排序链表中的重复元素 II
算法思想:
1、设置一个哑指针指向头结点,方便后续操作,设置一个移动指针p,哑指针指向p,判断链表长度是否小于2,如果小于2,就说明肯定没有重复元素,直接返回头结点
2、循环判断p指针的后两个结点是否为空,如果不为空,判断这连个结点的值是否相等。如果相等,记录下这个值,并循环判断p后面的结点是否等于这个值,只要相等就删除。如果不相等,后移p指针
3、返回哑结点的后一个结点

struct ListNode* deleteDuplicates(struct ListNode* head){
	ListNode *r=(struct ListNode*)malloc(sizeof(struct ListNode));
	r->next=head;
	if(head==NULL||head->next==NULL){
		return head;
	}
	struct ListNode *p=r;
	while(p->next!=NULL&&p->next->next!=NULL){
		if(p->next->val==p->next->next->val){
			int x=p->next->val;
			while(p->next!=NULL&&p->next->val==x){
				p->next=p->next->next;
			}
		}
		else{
			p=p->next;
		}
	}
	return r->next;
}

7、分隔链表
算法思想:
1、建立两个哑指针left和right分别来维护比x小的结点和大于等于x的结点
2、设置移动指针p初始指向头结点,只要p指针不为空,循环判断p指针对应结点的值,如果小于x,就加入到left链表中,否则加入到right链表,同时后移p指针。
3、把right链表加入到left链表队尾,把right队尾置空,返回left的头指针

struct ListNode* partition(struct ListNode* head, int x){
	struct ListNode* left=(struct ListNode*)malloc(sizeof(struct ListNode));
	struct ListNode* right=(struct ListNode*)malloc(sizeof(struct ListNode));
	struct ListNode *p=head,*l=left,*r=right;
	while(p!=NULL){
		if(p->val<x){
			l->next=p;
			l=l->next;
		}
		else{
			r->next=p;
			r=r->next;
		}
		p=p->next;
	}
    r->next=NULL;
	l->next=right->next;
	return left->next;
}

8、反转链表
算法思想:
使用头插法

struct ListNode* reverseList(struct ListNode* head){
	struct ListNode *dummy=(struct ListNode*)malloc(sizeof(struct ListNode));
    dummy->next=NULL;
    if(!head){
        return head;
    }
	struct ListNode *p=head,*r=dummy,*q=head->next;
	while(p){
		q=p->next;
		p->next=r->next;
		r->next=p;
		p=q;
	}
	return dummy->next;
}

9、环形链表 II
算法思想:
设置快慢指针p,q,如果有环的话,p和q会相遇,如果没有环的话,p,q指向空结点就结束

10、重排链表

算法思想:
寻找链表中点 + 链表逆序 + 合并链表

11、对链表进行插入排序
算法思想:
1、维护一个有序序列,初始时创建一个哑结点,指向头结点,再设置一个指针q指向头结点后一个结点,断开头结点与后面的结点。这样就构成了两个链表,其中前面哑指针指向的链表是有序的。
2、为了防止断链,设置一个r指针指向q指针后面一个位置。然后拿着q指针指向结点的值,去遍历前面这个有序链表,找到合适的位置插入进去。
3、把q指针指向r指针指向的位置,r再指向q后面的一个位置,循环往复去插入,直到后面这个链表所有指针都被插入到有序链表中。

struct ListNode* insertionSortList(struct ListNode* head){
	if(!head||!head->next){
		return head;
	}
	struct ListNode *t=(struct ListNode*)malloc(sizeof(struct ListNode));
	t->next=head;
	struct ListNode *p=t,*q=head->next,*r;
	head->next=NULL;
	while(q){
		r=q->next;
		while(p->next&&q->val>p->next->val){
			p=p->next;
		}
		q->next=p->next;
		p->next=q;
		p=t;
		q=r;
	}
	return t->next;
	
}

12、相交链表
算法思想
1、设置移动指针p,q分别指向两个头结点,同时遍历两个链表得到链表长度
2、选择链表较长的那个链表,后移指针至剩余链表长度与另一个链表长度相同的位置
3、同时后移p,q指针,循环比较是否移动到了相同结点,如果到循环结束都未能移动到相同结点,说明不存在相交节点

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
	struct ListNode *p=headA,*q=headB;
	int len1=0,len2=0;
	while(p){
		len1++;
		p=p->next;
	}
	while(q){
			len2++;
			q=q->next;
		}
	p=headA;
	q=headB;
	if(len1>len2){
		int x=len1-len2;
		while(x--){
			p=p->next;
		}
	}
	if(len2>len1){
			int x=len2-len1;
			while(x--){
				q=q->next;
			}
	}
	while(p&&q){
		if(p==q){
			return p;
		}
		p=p->next;
		q=q->next;
	}
    return NULL;
}

13、移除链表元素
算法思想:

1、设置一个哑结点,指向头结点,再设置移动结点p,q指向头结点和头结点后一个结点;
2、只要q结点不为空,就循环判断p指向的结点是否等于给定值val,如果相等,则把p结点指向q结点的后一个结点,并且后移q结点;如果不等,后移p,q结点
3、返回哑结点的后一个结点


struct ListNode* removeElements(struct ListNode* head, int val){
	struct ListNode* newhead=(struct ListNode*)malloc(sizeof(struct ListNode));
	newhead->next=head;
	struct ListNode* p=newhead,*q=head;
	while(q){
		if(q->val==val){
			p->next=q->next;
		}
		else{
			p=p->next;
		}
		q=q->next;
		
	}
	return newhead->next;
}

14、回文链表
算法思想:
把链表的值赋值到数组中,然后遍历链表判断是否回文

bool isPalindrome(struct ListNode* head){
	int arr[100000];
	int len=0;
	while(head){
		arr[len++]=head->val;
		head=head->next;
	}
	for(int i=0;i<len/2;i++){
		if(arr[i]!=arr[len-i-1]){
			return false;
		}
	}
	return true;
}

15、 删除链表中的节点
这题思想巧妙
算法思想:
把给定结点下一个结点的值赋给给定结点,然后删除下一个结点

void deleteNode(struct ListNode* node) {
    struct ListNode* p=node->next;
    node->val=p->val;
    node->next=p->next;
}

16、奇偶链表
算法思想:
1、设置一个偶数结点的头结点,用于存放所有的偶数索引
2、遍历链表,把每个偶数结点放入偶数链表
3、剩下的结点都是奇数索引结点,把偶数链表拼接到奇数链表最后

struct ListNode* oddEvenList(struct ListNode* head){
    	if(!head||!head->next)return head;
	struct ListNode *odd=(struct ListNode*)malloc(sizeof(struct ListNode));
	struct ListNode* p=head,*q=head->next,*r=odd;
	while(p->next&&p->next->next){
		q=p->next;
		p->next=p->next->next;
		r->next=q;
		r=r->next;
		p=p->next;
	}
	if(p->next){
		r->next=p->next;
        	r=r->next;
	}
	p->next=odd->next;
	r->next=NULL;
	return head;
}

17、链表的中间结点
算法思想:
使用快慢指针,快的指针走两部,慢的指针走一步,这样当快指针到链尾,慢指针走到中间位置

struct ListNode* middleNode(struct ListNode* head){
	struct ListNode* p=head,*q=head;
	while(q->next&&q->next->next){
		p=p->next;
		q=q->next->next;
	}
	if(q->next){
		
		return p->next;
	}
	return p;

}

二、二叉树

1、二叉树的中序遍历

void fun(struct TreeNode* root,int* arr,int* returnSize){
	if(root==NULL){
		return;
	}
	fun(root->left,arr,returnSize);
	arr[(*returnSize)++]=root->val;
	fun(root->right,arr,returnSize);
	return;
}
int* inorderTraversal(struct TreeNode* root, int* returnSize){
	 int* res = (int*)malloc(sizeof(int) * 501);
	 *returnSize=0;
	 fun(root,res,returnSize);
	 return res;
}

2、 验证二叉搜索树


bool fun(struct TreeNode* root,long  min,long max){
	if(!root){
		return true;
	}
	if(root->val<=min||root->val>=max){
		return false;
	}
	
	return fun(root->left,min,root->val)&&fun(root->right,root->val,max);
}
bool isValidBST(struct TreeNode* root){
	return fun(root,-10000000000,10000000000);
	}

3、相同的树

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
	if(!p&&!q){
		return true;
	}else if(!p||!q){
		return false;
	}else if(p->val!=q->val){
		return false;
	}else{
		return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
	}
}

4、对称二叉树

bool fun(struct TreeNode* p, struct TreeNode* q){
	if(!p&&!q){
		return true;
	}else if(!p||!q){
		return false;
	}else if(p->val!=q->val){
		return false;
	}else{
		return fun(p->left,q->right)&&fun(p->right,q->left);
	}
}
bool isSymmetric(struct TreeNode* root){
	struct TreeNode* p=root,*q=root;
	return fun(p,q);
}

5、二叉树的最大深度



void fun(struct TreeNode* root,int high,int* height){
	
	if(!root){
		return;
	}
	high++;
	fun(root->left,high,height);
	fun(root->right,high,height);
	if(high>*height){
		*height=high;
	}
	return;
}
int maxDepth(struct TreeNode* root){
	int height=0;
	fun(root,0,&height);
	return height;
}

6、平衡二叉树


int height(struct TreeNode* root) {
    if (root == NULL) {
        return 0;
    } else {
        return fmax(height(root->left), height(root->right)) + 1;
    }
}

bool isBalanced(struct TreeNode* root) {
    if (root == NULL) {
        return true;
    } else {
        return fabs(height(root->left) - height(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
    }
}

7、二叉树的最小深度

int minDepth(struct TreeNode* root){
if(!root)return 0;
int height=100000;
	fun(root,0,&height);
	return height;
}
void fun(struct TreeNode* root,int high,int* height){
	
	if(!root){
		return;
	}
	high++;
	fun(root->left,high,height);
	fun(root->right,high,height);
	if(high<*height&&!root->left&&!root->right){
		*height=high;
	}
	return;
}

8、路径总和


void fun(struct TreeNode* root,int targetSum,int num,int* sign){
	if(!root){
		return;
	}
	num+=root->val;
	if(num==targetSum&&!root->left&&!root->right){
		*sign=1;
	}
	fun(root->left,targetSum,num,sign);
	fun(root->right,targetSum,num,sign);
	return;
}

bool hasPathSum(struct TreeNode* root, int targetSum){
	if(!root)return false;
	int sign=0;
	fun(root,targetSum,0,&sign);
	if(sign==1){
		return true;
	}else{
		return false;
	}
}

9、求根节点到叶节点数字之和

void fun(struct TreeNode* root,int* height,int h){
	if(!root){
		return;
	}
	h=h*10+root->val;
	
	if(!root->left&&!root->right){
		*height+=h;
		return;
	}
	fun(root->left,height,h);
	fun(root->right,height,h);
	return;
}

int sumNumbers(struct TreeNode* root){
    int height=0;
	if(!root)return 0;
	fun(root,&height,0);
	return height;
}

10、翻转二叉树


struct TreeNode* invertTree(struct TreeNode* root){
	if(!root){
		return NULL;
	}
	struct TreeNode *left=invertTree(root->left);
	struct TreeNode *right=invertTree(root->right);
	root->left=right;
	root->right=left;
	return root;
}

11、二叉搜索树中第K小的元素
12、二叉搜索树的最近公共祖先
12、二叉树的最近公共祖先
14、左叶子之和

void fun(struct TreeNode* root,int* returnSize){
	if(!root){
		return;
	}
	if(root->left&&!root->left->left&&!root->left->right){
		*returnSize+=root->left->val;
	}
	fun(root->left,returnSize);
	fun(root->right,returnSize);
	return;
}

int sumOfLeftLeaves(struct TreeNode* root){
	int returnSize=0;
	fun(root,&returnSize);
	return returnSize;
}

15、删除二叉搜索树中的节点
16、找树左下角的值

void fun(struct TreeNode* root,int* returnSize,int high,int* height){
	
	if(!root){
		return;
	}
	high++;
	fun(root->left,returnSize,high,height);
	fun(root->right,returnSize,high,height);
	if(high>*height){
		*height=high;
		*returnSize=root->val;
	}
	return;
}

int findBottomLeftValue(struct TreeNode* root){
	int returnSize=root->val;
	int height=0;
	fun(root,&returnSize,0,&height);
	return returnSize;
}

17、把二叉搜索树转换为累加树
18、二叉树的直径
19、合并二叉树
20、二叉搜索树中的搜索


struct TreeNode* searchBST(struct TreeNode* root, int val){
	while(root){
		if(root->val==val){
			return root;
		}
		else if(root->val>val){
			root=root->left;
		}
		else{
			root=root->right;
		}
	}
	return NULL;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值