数据结构与算法的练习日记
前言:
- 1-3
- 4-
一、Leetcode
1.leetcode 24 两两交换链表中的节点
题目:给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
/**
思路:
*/
class Solution {
public ListNode swapPairs(ListNode head) {
if(head==null||head.next==null){
return head;
}
ListNode dummyHead=new ListNode(0);
dummyHead.next=head;
ListNode temp=dummyHead;
while(temp.next!=null && temp.next.next!=null){//能创建出着两个新节点
ListNode node1=temp.next;
ListNode node2=temp.next.next;
temp.next=node2;
node1.next=node2.next;
node2.next=node1;
temp=node1;
}
return dummyHead.next;
}
}
2.leetcode 283 移动零
题目:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
- 开始的错误解法:
class Solution {
public void moveZeroes(int[] nums) {
int j=nums.length-1;
int i=0;
while(i<j){//双指针
while(nums[j]==0){
j--;
}
if(nums[i]==0){
int temp=nums[i];
nums[i] = nums[j];
nums[j] = temp;
j--;
}
i++;
}
}
}
- 正确的解法:
class Solution {
public void moveZeroes(int[] nums) {
int n = nums.length, left = 0, right = 0;
while (right < n) {
if (nums[right] != 0) {//如果右指针不是0;左指针的左边都是非0的;
swap(nums, left, right);//让0都在左右指针中间
left++;
}
right++;//右指针不断向右移动
}
}
public void swap(int[] nums, int left, int right) {
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
}
3 leetcode 485 最大连续 1 的个数
题目:给定一个二进制数组 nums , 计算其中最大连续 1 的个数。
class Solution {
public int findMaxConsecutiveOnes(int[] nums) {
int maxCount =0;
int count =0;
int n=nums.length;
for(int i=0;i<n;i++){
if(nums[i] == 1) {//如果1,就加;
count++;
}else{//如果是0,就清零,比较一次;
maxCount=Math.max(maxCount,count);
count=0;
}
}
maxCount=Math.max(maxCount,count);
return maxCount;
}
}
4 leetcode 203移除链表元素
题目:给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
while(head!=null && head.val==val ){//不能写反
head=head.next;
}
if(head == null){//然后再判断一下边界
return head;
}
ListNode temp=head;
while(temp.next!=null){
ListNode tempNext=temp.next;
if(tempNext.val==val){
temp.next=tempNext.next;
}else{
temp=temp.next;
}
}
return head;
}
}
5 leetcode 160相交链表
题目:给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
//把所有headA 链表添加 到hashSet 中然后headB中 有则就结束
Set<ListNode> visited =new HashSet<ListNode>();
ListNode temp=headA;
while(temp!=null){
visited.add(temp);
temp=temp.next;
}
temp=headB;
while(temp!=null){
if(visited.contains(temp)){
return temp;
}
temp=temp.next;
}
return null;
}
}
6 leetcode 150. 逆波兰表达式求值
题目:给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
- 逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。
该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。
逆波兰表达式主要有以下两个优点:
去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
/**
适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
*/
import java.util.Stack;
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack =new Stack<>();
int length=tokens.length;
for(int i=0;i<length;i++){
String token=tokens[i];
if(isNumber(token)){
stack.push(Integer.parseInt(token));//String类型转化为Integer
}else {
int num2=stack.pop();
int num1=stack.pop();
switch (token) {
case "+":
stack.push(num1 + num2);
break;
case "-":
stack.push(num1 - num2);
break;
case "*":
stack.push(num1 * num2);
break;
case "/":
stack.push(num1 / num2);
break;
default:
}
}
}
return stack.pop();
}
public boolean isNumber(String token){
return !("+".equals(token)||"-".equals(token)||"*".equals(token)||"/".equals(token));
}
}
7 leetcode 141环形链表(是否有无环)
题目:给你一个链表的头节点 head ,判断链表中是否有环。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode temp1=head;
ListNode temp2=head;
//判断是否有环,使用快慢指针,空间复杂度最低
//也可以用hashset法
//最笨的用两重循环一次比较一个节点前面是否出现
if(temp1==null){
return false;
}
if(temp2.next==null||temp2.next.next==null){
return false;
}
while(temp2!=null&&temp2.next!=null){//temp2跑得快
temp1=temp1.next;//先后移后判断
temp2=temp2.next.next;
if(temp1==temp2){
return true;
}
}
return false;
}
}
8 leetcode 9回文数
题目:给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
例如,121 是回文,而 123 不是。
class Solution {
public boolean isPalindrome(int x) {
//转换为字符串(回文字符串),就很简单
String str1=String.valueOf(x); //x.toString()
// String str2=x+"";
// return str1.equals(str2.reverse());//String 是不可变,没有reverse(),要用StringBuilder来新建字符串
StringBuilder strbu=new StringBuilder();
strbu.append(str1);//要这样添加
return str1.equals(strbu.reverse().toString());
}
}
9 leetcode
题目:
10 leetcode
题目:
11 leetcode
题目:
12 leetcode
题目:
13 leetcode
题目:
14 leetcode
题目:
二、Acwing
1.Acwing
三、其他练习:
1.用hashSet 判断字符串是否有重复元素;
package Practice;
import java.util.HashSet;
public class CharacterRepeat {
public static boolean isRepeat(String str){
int length=str.length();
for(int i=0;i<length;i++){
for(int j=i+1;j<length;j++){
if(str.charAt(i)==str.charAt(j)){
return true;
}
}
}
return false;
}
public static boolean isRepeat2(String str){
int length=str.length();
HashSet<Character> set=new HashSet<>();
for(int i=0;i<length;i++){
if(set.contains(str.charAt(i))){
return true;
}else{
set.add(str.charAt(i));
}
}
return false;
}
public static void main(String[] args) {
boolean result=isRepeat2("abcdefg");
System.out.println(result);
boolean result2=isRepeat2("abcdefgabc");
System.out.println(result2);
}
}
2.用hashMap 计算一个字符串每种字符有多少个;
package Practice;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class StrCounter {
public static HashMap<Character,Integer> getStrCounter(String str){
HashMap<Character,Integer> counter=new HashMap<>();
int length=str.length();
for(int i=0;i<length;i++){
Character c=str.charAt(i);
if(counter.containsKey(c)){
Integer count=counter.get(c);
count++;
counter.put(c,count);
}else{
counter.put(c,1);
}
}
return counter;
}
public static void main(String[] args) {
HashMap<Character,Integer> counter=new HashMap<>();
counter=getStrCounter("abcdefgabcd");
for(Map.Entry<Character,Integer> entry:counter.entrySet()){
System.out.println(entry.getKey()+" "+entry.getValue());
}
}
}
3.用Stack 反转字符串;
package Practice;
import java.util.Stack;
public class StrReverse {
public static String reverseStr(String str) {
Stack<Character> stack = new Stack<>();
int length = str.length();
for (int i = 0; i < length; i++) {
char c = str.charAt(i);
stack.push(c);
}
String newStr = new String();
while (!stack.empty()) {
newStr += stack.pop();
}
return newStr;
}
public static void main(String[] args) {
String s = reverseStr("abcdefg");
System.out.print(s);
}
}
4.反转链表
package Practice;
public class reverseLinkedList {
/**
* 反转链表非递归方法:
* 1.定义三个指针,分别指向当前节点,当前节点的前一个节点,当前节点的后一个节点
* 2.后一个节点先保存起来当前节点的下一个节点,(让后移知道地方);
* 3.然后开始反转,当前节点的下一个节点指向pre节点,然后pre节点,和当前节点都向后移动;
* 4.当前节点指向后一个节点,继续循环,直到当前节点为空;
* 5.最后返回pre节点;
*
* 备注:每次只有一个指针转换,然后两个指针后移(后移到cur和next);
* @param head
* @return
*/
public static ListNode reverseLinkedList(ListNode head) {
ListNode pre = null;//前一个节点
ListNode cur = head;//当前节点
while (cur != null) {
ListNode next = cur.next;//后一个节点,让下面的后移操作知道地方
cur.next = pre;//反转
pre = cur;//后移
cur = next;//后移
}
return pre;
}
public static void main(String[] args) {
ListNode head = new ListNode(1);
ListNode node1 = new ListNode(2);
ListNode node2 = new ListNode(3);
ListNode node3 = new ListNode(4);
head.next = node1;
node1.next = node2;
node2.next = node3;
ListNode list = reverseLinkedList(head);
while (list != null) {
System.out.print(list.val + " ");
list = list.next;
}
}
}
class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
}
}
5.
四、递归与递推联系:
1.递归实现指数型枚举
题目:从 1∼n 这 n个整数中随机选取任意多个,输出所有可能的选择方案
-算法思路:递归树,每个数字可以选可以不选(一个位置一个位置的数,往后深度遍历)
递归函数dfs:该函数接收一个参数u,表示当前正在处理排列中的第u个位置。
- 基准情况(*边界结束条件*):如果u > n,说明已经生成了一个完整的排列,此时打印出该排列并返回。
- 递归过程(*深度遍历递归*):从1到n遍历所有数字,对于每一个数字i:
--- 检查是否使用过:如果i没有被使用过(即used[i]为false),则进行以下操作:
* 将i放入当前位置u(即state[u] = i)。
* 标记i为已使用(即used[i] = true)。
* 递归调用dfs(u + 1)以处理下一个位置。
* 回溯:在返回到当前层之前,需要撤销之前的操作,即将state[u]重置为0,used[i]重置为 false,以便进行下一个分支的搜索。
import java.util.Scanner;
public class Main{
public static int n;
public static int[] state;
public static void dfs(int u){
if(u>n){//最后一层,已经生成了一组数,可以输出结束了;
for(int i=1;i<=n;i++){
if(state[i]==2){
System.out.print(i+" ");
}
}
System.out.println();
return;
}
state[u]=1;//1是不选,2是选
dfs(u+1);
state[u]=0;
state[u]=2;
dfs(u+1);
state[u]=0;
}
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
state=new int[n+1];
dfs(1);
}
}
2.递归实现排列型枚举
题目:把 1∼n 这 n个整数排成一行后随机打乱顺序,输出所有可能的次序。
算法思路:-依然递归搜索树,(循环依次)一个位置一个位置选(“深度遍历”),结束(到达边界)的时候将所有位置的数打印出来
import java.util.Scanner;
public class Main{
public static int n;//一共n个数字
public static int[] state;//每一个位置u的存入的数i,
public static boolean[] used;//判断是否使用过;
public static void dfs(int u){
if(u>n){
for(int i=1;i<=n;i++){
System.out.print(state[i]+" ");
}
System.out.println();
return;
}
for(int i=1;i<=n;i++){//从一到都要进行递归分支
if(!used[i]){//如果没有用过
state[u]=i;//第一个位置,写入i,
used[i]=true;//i代表用过了
dfs(u+1);
state[u]=0;//回复状态
used[i]=false;
}
}
}
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
state=new int[n+1];
used=new boolean[n+1];
dfs(1);//递归搜索树
}
}
3、递归实现组合型枚举
题目:从 1∼n 这 n个整数中随机选出 m个,输出所有可能的选择方案
import java.util.Scanner;
//基本和其他几道递归题目一样,只有一步要注意,看文中注释的地方
public class Main{
public static int m,n;
public static int[] state;
public static void dfs(int u){
if(u>m){
for(int i=1;i<=m;i++){//依然是一位一位填数字
System.out.print(state[i]+" ");
}
System.out.println();
return;
}
for(int i=state[u-1]+1;i<=n;i++){//要i从上一位的数值大小开始
state[u]=i;
dfs(u+1);
state[u]=0;
}
}
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
state=new int[m+1];
dfs(1);
}
}
4.简单的斐波那契数列输出
方法1:
//用递推计算
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int n;
n=sc.nextInt();
int[] f=new int[n];
f[0]=0;
if(n==1){
System.out.print(f[0]);
}else if(n==2){
f[1]=1;
System.out.print(f[0]+" "+f[1]+" ");
}else{
f[1]=1;
System.out.print(f[0]+" "+f[1]+" ");
for(int i=2;i<n;i++){
f[i]=f[i-1]+f[i-2];
System.out.print(f[i]+" ");
}
}
}
}
方法2:
//两个数交替滚动计算
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int a=0;
int b=1;
for(int i=1;i<=n;i++){
System.out.print(a+" ");
int sum=a;
a=b;
b+=sum;
}
}
5、(快排小练习)
import java.util.Scanner;
public class Main{
public static void quickSort(int[] a,int left,int right){
if(left>=right){//边界条件,如果left>=right:说明已经拍好顺序了
return;
}
int i=left-1;
int j=right+1;
int mid=a[left+right>>1];
while(i<j){
do i++;while(a[i]<mid);//两个指针如果满足条件就向中间移动,如果都不满足,就交换两个数
do j--;while(a[j]>mid);
if(i<j){
int temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
quickSort(a,left,j);//分别递归排 mid 左边,和右边
quickSort(a,j+1,right);
}
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int k=sc.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++){
a[i]=sc.nextInt();
}
quickSort(a,0,n-1);
System.out.print(a[k-1]);
}
}
6、二分查找
题目:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
class Solution {
public int search(int[] nums, int target) {
int length=nums.length;
int left=0;
int right=length-1;
while(left<=right){//索引就是指针
int mid=left+right>>1;
if(target==nums[mid]){
return mid;
}else if(target>nums[mid]){
left=mid+1;
}else{
right=mid-1;
}
}
return -1;
}
}