算法笔试题面试题
算法笔试面试
十大排序算法:冒泡排序,选择排序,插入排序,归并排序,堆排序,快速排序、希尔排序、计数排序,基数排序,桶排序。
ps:重点在理解原理,写代码的时候要由里往外写。
冒泡排序:
思想:两个相邻的元素比较并交换。
public static void bubbleSort(int[] arr) {
if(arr.length == 0){
return;
}
// 一共进行元素-1次排序
for (int i = 0; i < arr.length -1; i++) {
// 只需要对没有排序的进行排序
for (int j = 0; j < arr.length - 1 - i; j++) {
//将前一个比后一个大的两元素进行交换
if (arr[j+1] < arr[j]) {
// int tmp = arr[j+1];
// arr[j+1] = arr[j];
// arr[j] = tmp;
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
public static void main(String[] args) {
int arr[] = {5, 8, 6, 3, 9, 2, 1, 7};
bubbleSort(arr);
System.out.println(Arrays.toString(arr));
}
public class Test {
public static void main(String[] args) {
int[] nums = {1, 3, 4, 7, 2, 9};
for (int i = 0; i < nums.length; i++) {
for(int j = i + 1; j < nums.length; j++){
if(nums[i] < nums[j]){ //降序排列
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}
Arrays.stream(nums).forEach(System.out::println);
}
}
选择排序
思想:每次从剩余元素中找最大(最小)元素进行交换。
选择排序可以看做是冒泡排序的一种优化。
public class Solution {
public int[] choiceSort(int[] nums){
if(nums.length == 0){
return nums;
}
for (int i = 0; i < nums.length; i++) {
// 最小数的下标,每个循环开始总是假设第一个数最小
int minIndex = i;
for (int j = i; j < nums.length; j++) {
if(nums[j] < nums[minIndex]){
minIndex = j;
}
}
// 进行元素的交换
int temp = nums[minIndex];
nums[minIndex] = nums[i];
nums[i] = temp;
}
return nums;
}
public static void main(String[] args) {
int arr[] = {5, 8, 6, 3, 9, 2, 1, 7};
Solution solution = new Solution();
int[] ret = solution.choiceSort(arr);
Arrays.stream(ret).forEach(System.out::println);
}
}
插入排序
思想:就是将要待排序的元素,在已经排好序的序列中找到合适的位置进行插入即可。
对于基本有序的数组来说最好用。
思想:就好像是打麻将或者是玩扑克牌。只不过略微有点区别。
public static void insertSort(int[] arr) {
if(arr.length == 0){
return;
}
for (int i = 1; i < arr.length; i++) {
for (int j = i ; j >0; j--) {
if(arr[j ] < arr[j - 1]){
int tmp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = tmp;
}
}
System.out.println(Arrays.toString(arr));
}
}
public static void main(String[] args) {
int arr[] = {5, 8, 6, 3, 9, 2, 1, 7};
insertSort(arr);
System.out.println(Arrays.toString(arr));
}
第二种写法:// TODO 待理解
public class Solution {
public int[] insertSort(int[] nums){
// 健壮性校验
if(nums.length == 0){
return nums;
}
// 当前待排序数据,该元素之前的元素均已被排序过
int currentValue;
for (int i = 0; i < nums.length -1; i++) {
// 已被排序数据的索引
int preIndex = i;
currentValue = nums[preIndex + 1];
// 在已被排序过数据中倒序寻找合适的位置,如果单管待排序数据比比较的元素要小,将比较的元素后移一位
while (preIndex >= 0 && currentValue < nums[preIndex]){
// 将当前元素后移一位
nums[preIndex + 1] = nums[preIndex];
preIndex--;
}
// while循环结束时,说明已经找到了当前待排序数据的合适位置,插入
nums[preIndex + 1] = currentValue;
}
return nums;
}
public static void main(String[] args) {
int arr[] = {5, 8, 6, 3, 9, 2, 1, 7};
Solution solution = new Solution();
int[] ret = solution.insertSort(arr);
Arrays.stream(ret).forEach(System.out::println);
}
}
快速排序
思想:首先选择一个基准数,然后将数组中剩余的数据依次和基准数进行比较,所有比基准数小的放到基准数前面,所有比基准数大的元素放到基准数后面,这样就将整个数组分成了两部分,然后再将这每一部分分别进行快速排序,说白了就是运用了一种分而治之的思想。
视频地址:https://www.bilibili.com/video/BV1Ey4y1k7s1/?spm_id_from=333.337.search-card.all.click&vd_source=273847a809b909b44923e3af1a7ef0b1
public class Solution {
/**
* 快速排序
* @param arr 待排序数组
* @param left 左指针
* @param right 右指针
* @return
*/
public int[] quickSort(int[] arr, int left, int right) {
// 健壮性校验
if (arr.length == 0) {
return arr;
}
int l = left;
int r = right;
// 基准值
int pivot = arr[(left+right)/2];
while (l<r){
// 如果左指针位置的数字小于基准值,就让左指针往后移动
while (arr[l]< pivot){
l++;
}
// 如果右指针位置的数字大于基准值,就让右指针往前移动
while (arr[r] > pivot){
r--;
}
// 特殊情况:如果左指针大于右指针的话,就跳出循环
if(l >= r){
break;
}
// 如果pivot轴左右指针位置对应的数字,左指针对应的数字大于右指针对应的数字,就将这两个位置的数字进行交换
int temp = arr[r];
arr[r] = arr[l];
arr[l] = temp;
// 还会有两种特殊情况,就是左指针对应的数字等于pivot值,右指针对应的数字等于pivot值
if(arr[l] == pivot){
r--;
}
if(arr[r] == pivot){
l++;
}
}
if(l == r){
l++;
r--;
}
if(left<r){
quickSort(arr,left,r);
}
if(l<right){
quickSort(arr,l,right);
}
return arr;
}
public static void main(String[] args) {
int arr[] = {5, 8, 6, 3, 9, 2, 1, 7};
Solution solution = new Solution();
int[] ret = solution.quickSort(arr, 0, arr.length-1);
Arrays.stream(ret).forEach(System.out::println);
}
}
希尔排序
希尔排序是一种基于插入排序的算法,通过将整个数组分成多个子序列进行插入排序来提高排序的效率。说白了就是分成gap组。
一开始将整个待排序列分成gap= nums.length/2组,接着会分成gap= gap/2组,直到最后分成一组,将整个待排序数组进行一次插入排序就好了。

归并排序
思想:采用的是先分再治的思想,就是将待排序数组分成两块,然后每一块再进行划分,直到划分的每一块都只有一个元素的时候,这个时候再进行合并,当然合并的过程中要进行排序。
堆排序
适用场景:比方说我们有一个待排序数组,但是我们可能只是想要其中的最大值或者最小值,也就是说没有必要将整个数组都排好序,这个时候我们就可以使用堆排序了。
底层使用到了二叉树。
计数排序
计数排序是一种不比较元素大小的排序算法。
计数排序对一定范围内的整数排序时候的速度非常快,一般快于其他排序算法。但是计数排序局限性比较大,只限于对整数进行排序,而且待排序元素值分布较连续、跨度小的情况。
如果一个数组里面所有元素都是整数,而且都在0-k以内,那对于数组里每个元素来说,如果能知道数组里有多少项小于或等于该元素,就能够准确地给出该元素在排序后的数组中的位置。
除了待排序数组之外,还需要引入一个计数数组。
桶排序
桶排序是计数排序的升级版。
工作原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序)。
基数排序
原理:利用个位、十位、百位的方式进行排序,说白了就是个位安排0-9个桶,十位也是,百位也是,等等。
leecode 二分查找
二分查找要求数组必须是有序的。
方式一:使用循环的方式
public class Solution {
/**
* 二分查找
*
* @return
*/
public int binarySearch(int[] arr, int target) {
int start = 0;
int end = arr.length - 1;
// 循环查找
while (start <= end) {
int mid = (start + end) / 2;
if (target > arr[mid]) {
start = mid + 1;
} else if (target < arr[mid]) {
end = mid - 1;
} else { // 表示找到了
return mid;
}
}
// -1 表示没有查找到
return -1;
}
public static void main(String[] args) {
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int target = 7;
Solution solution = new Solution();
int ret = solution.binarySearch(arr, target);
System.out.println(ret);
}
}
// TODO 方式二,采用递归方式。
爬楼梯
方法一:递归解法
public static int climbStairs(int n) {
if(n == 0){return 0;}
if(n == 1){return 1;}
if(n == 2){return 2;}
return climbStairs(n-1) + climbStairs(n-2);
}
public static void main(String[] args) {
System.out.println(climbStairs(3));
}
以上的解法存在重复计算的问题,我们可以使用hashmap集合来保存我们已经计算过的结果。
private static HashMap<Integer,Integer> map = new HashMap();
public static int climbStairs2(int n) {
if(n == 0){return 0;}
if(n == 1){return 1;}
if(n == 2){return 2;}
if(null != map.get(new Integer(n))){
return map.get(new Integer(n));
}
int reslut = climbStairs(n-1) + climbStairs(n-2);
map.put(new Integer(n),reslut);
return reslut;
}
## 合并两个排序的链表
输入两个递增排序的链表,合并后的这两个链表中的节点仍然是递增排序的。
public class Test {
private static class ListNode<E>{
E val;
ListNode<E> next;
public ListNode(E val){
this.val = val;
}
public ListNode(E val,ListNode<E> next){
this.val = val;
this.next = next;
}
}
private static void print(ListNode head){
while (null != head){
System.out.print("\t" + head.val);
head = head.next;
}
}
private ListNode<Integer> mergeTwoLists(ListNode<Integer> head1, ListNode<Integer> head2){
ListNode<Integer> dummy = new ListNode<>(-1);
ListNode<Integer> pre = dummy;
while(head1 != null && head2 != null){
if(head1.val <= head2.val){
pre.next = head1;
head1 = head1.next;
}else {
pre.next = head2;
head2 = head2.next;
}
pre = pre.next;
}
if(null == head1){
pre.next = head2;
}
if(null == head2){
pre.next = head1;
}
return dummy.next;
}
public static void main(String[] args) {
ListNode<Integer> list1 = new ListNode<>(1);
ListNode<Integer> list2 = new ListNode<>(3);
ListNode<Integer> list3 = new ListNode<>(5);
ListNode<Integer> list4 = new ListNode<>(7);
ListNode<Integer> list5 = new ListNode<>(9);
list1.next = list2;
list2.next = list3;
list3.next = list4;
list4.next = list5;
ListNode<Integer> l1 = new ListNode<>(2);
ListNode<Integer> l2 = new ListNode<>(4);
ListNode<Integer> l3 = new ListNode<>(6);
ListNode<Integer> l4 = new ListNode<>(8);
ListNode<Integer> l5 = new ListNode<>(10);
l1.next = l2;
l2.next = l3;
l3.next = l4;
l4.next = l5;
Test test = new Test();
ListNode<Integer> reslut = test.mergeTwoLists(list1, l1);
print(reslut);
}
}
请写出Java代码实现如下功能:读取一篇文章,输出其中出现次数最多的单词。
public class Test {
public static void main(String[] args) {
List<String> words = new ArrayList<>();
words.add("hello");
words.add("world");
words.add("goods");
words.add("world");
words.add("world");
words.add("world");
words.add("world");
Map<String, Integer> countMap = new HashMap<>();
for (int i = 0; i < words.size(); i++) {
if(countMap.get(words.get(i)) == null){
countMap.put(words.get(i), 1);
}else {
countMap.put(words.get(i), countMap.get(words.get(i)) + 1);
}
}
int maxCount = 0;
String maxCountWord = null;
// 遍历map
for(Map.Entry<String, Integer> entry : countMap.entrySet()){
if(maxCount < entry.getValue()){
maxCount = entry.getValue();
maxCountWord = entry.getKey();
}
}
System.out.println("出现次数最多的单词是:"+ maxCountWord + "出现次数:" + maxCount);
}
}
请写代码实现s=1+2-3+4-5+6-7...n请编写一个函数int calc(int n),传入参数n,返回s的值.
public class Test {
public static int calc(int n){
int sum = 1;
for (int i = 2; i <= n; i++) {
if(i % 2 == 0){
sum += i;
}else {
sum -= i;
}
}
return sum;
}
public static void main(String[] args) {
System.out.println(Test.calc(5));
}
}
请写出Java代码,实现从一个无序数组中找到最大的5个元素
// 总体思路是先进行冒泡排序,之后再找出前五个元素。
public class Test {
public static int[] calc(int[] arr) {
for (int j = 0; j < arr.length; j ++) {
for (int i = 0; i < arr.length - 1 - j; i++) {
int temp = 0;
if (arr[i] < arr[i + 1]) {
temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
}
return arr;
}
public static void main(String[] args) {
int[] arr = {5, 8, 2, 3, 9, 7, 4, 6, 10};
Test.calc(arr);
for (int i = 0; i < 5; i++) {
System.out.print("\t" + arr[i]);
}
}
}
找出两数之和的下标
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出何为目标值的那两个整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中的同一个元素不能使用两遍。
你可以按任意顺序返回答案。
方法一:使用暴力枚举
public class Test {
// 方法一:使用暴力枚举
public static int[] twoSum(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length ; j++) {
if(nums[i] + nums[j] == target){
System.out.println(nums[i] + " + " + nums[j] + " = " + (nums[i] + nums[j]));
return new int[]{i, j};
}
}
}
// 如果没有找到就返回0
return new int[]{0};
}
public static void main(String[] args) {
int[] nums = {2,7,11,15};
int target = 9;
// 输出结果:[0,1]
Arrays.stream(twoSum(nums, target)).forEach(System.out::println);
}
}
public class Test {
// 方法二:使用map进行映射
public static int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
int[] result = new int[2];
for (int i = 0; i < nums.length; i++) {
map.put(nums[i], i);
}
for (int i = 0; i < nums.length; i++) {
// map.get(target - nums[i]) != i 也就是说找到的另外一个元素一定不能是第一个元素
if(map.containsKey(target - nums[i]) && map.get(target - nums[i]) != i){
result[0] = i;
result[1] = map.get(target - nums[i]);
}
return result;
}
// 如果没有找到就返回0
return new int[]{0};
}
public static void main(String[] args) {
int[] nums = {2, 7, 11, 15};
int target = 9;
// 输出结果:[0,1]
Arrays.stream(twoSum(nums, target)).forEach(System.out::println);
}
}
public class Test {
// 方法三:还是使用map进行映射,只不过在第二种的基础上进行了简化。
public static int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
int[] result = new int[2];
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(target - nums[i])) {
result[0] = i;
result[1] = map.get(target - nums[i]);
return result;
}
map.put(nums[i], i);
}
// 如果没有找到就返回0
return new int[]{0};
}
public static void main(String[] args) {
int[] nums = {2, 7, 11, 15};
int target = 9;
// 输出结果:[0,1]
Arrays.stream(twoSum(nums, target)).forEach(System.out::println);
}
}
找出所有的两数之和的下标
package com.xs.designs.suanfa;
import java.util.*;
public class TwoSum {
public static List<List<Integer>> findTwoSumIndices(int[] nums, int target) {
// 存储结果(去重)
Set<List<Integer>> resultSet = new HashSet<>();
// 哈希表:key=元素值,value=元素下标
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
// 检查补数是否在哈希表中
if (map.containsKey(complement)) {
// 取出补数的下标 j
int j = map.get(complement);
// 存储下标对(按升序排列,避免重复)
List<Integer> pair = new ArrayList<>();
pair.add(Math.min(i, j));
pair.add(Math.max(i, j));
resultSet.add(pair);
}
// 将当前元素存入哈希表(值 -> 下标)
map.put(nums[i], i);
}
// 转换为 List 并返回
return new ArrayList<>(resultSet);
}
public static void main(String[] args) {
// 测试用例
int[] nums = {2, 7, 11, 15, 3, 6};
int target = 9;
List<List<Integer>> result = findTwoSumIndices(nums, target);
System.out.println(result); // 输出:[[0, 1], [4, 5]]
}
}
链表翻转
第一种方式:通过改动指针的方式
public class Test {
// 定义链表节点和指针
static class ListNode{
int val;
ListNode next;
ListNode(int val){
this.val = val;
}
ListNode(int val, ListNode next){
this.val = val;
this.next = next;
}
}
public static void print(ListNode head){
while(head != null){
System.out.print("\t" + head.val);
head = head.next;
}
}
public static ListNode iterate(ListNode head){
ListNode prev = null;
ListNode next = null;
while(head != null){
next = head.next; // 先将当前节点的下一个节点保存到next变量中
head.next = prev; // 将当前节点的下一个节点指向前一个节点
prev = head;
head = next;
}
return prev;
}
public static void main(String[] args) {
ListNode listNode1 = new ListNode(1);
ListNode listNode2 = new ListNode(2);
ListNode listNode3 = new ListNode(3);
ListNode listNode4 = new ListNode(4);
ListNode listNode5 = new ListNode(5);
listNode1.next = listNode2;
listNode2.next = listNode3;
listNode3.next = listNode4;
listNode4.next = listNode5;
ListNode head = iterate(listNode1);
print(head);
}
}
public class Test {
// 定义链表节点和指针
static class ListNode{
int val;
ListNode next;
ListNode(int val){
this.val = val;
}
ListNode(int val, ListNode next){
this.val = val;
this.next = next;
}
}
public static void print(ListNode head){
while(head != null){
System.out.print("\t" + head.val);
head = head.next;
}
}
// 使用递归的方式
public static ListNode recursion(ListNode head){
if(head == null || head.next == null){
return head;
}
ListNode newhead = recursion(head.next);
head.next.next = head;
head.next = null;
return newhead;
}
public static void main(String[] args) {
ListNode listNode1 = new ListNode(1);
ListNode listNode2 = new ListNode(2);
ListNode listNode3 = new ListNode(3);
ListNode listNode4 = new ListNode(4);
ListNode listNode5 = new ListNode(5);
listNode1.next = listNode2;
listNode2.next = listNode3;
listNode3.next = listNode4;
listNode4.next = listNode5;
ListNode head = recursion(listNode1);
print(head);
}
}
数组中三个数的最大乘积
思路,先对数组进行排序,排完序之后,在进行计算前三个或者是后三个数的乘积值。
public class Test {
public static int multiMost(int[] nums) {
// 先对数组进行排序,先手写一遍冒泡排序吧
for (int i = 0; i < nums.length; i++) {
for (int j = 0 + i; j < nums.length; j++) {
if (nums[i] > nums[j]) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}
// 如果想偷懒直接使用 Arrays.sort(nums);
return Math.max(nums[0] * nums[1] * nums[2], nums[nums.length - 1] * nums[nums.length - 2] * nums[nums.length - 3]);
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6};
int result = multiMost(arr);
System.out.println(result);
}
}
TODO:还有另外一种算法,待看。
求斐波那契数列的第N为的值是多少
方法一:使用暴力递归算法
public class Test {
public static int calculate(int num) {
if(0 == num){
return 0;
}
if(1 == num){
return 1;
}
return calculate(num-1) + calculate(num -2);
}
public static void main(String[] args) {
System.out.println(calculate(10));
}
}
// TODO 还有两种算法待看
排列硬币
public class Test {
/**
*
* @param n 一共有的硬币数
* @return
*/
public static int arrangeCoins(int n) {
// i 表示第几层
for (int i = 1; i <= n; i++) {
n = n -i;
if(n <= i){
return i;
}
}
return 0;
}
public static void main(String[] args) {
System.out.println(arrangeCoins(10));
}
}
// TODO 还有另外两种算法,待看
判断链表中是否有环
方式一:使用set集合
public class Test {
private static class ListNode<E>{
E val;
ListNode<E> next;
public ListNode(E val){
this.val = val;
}
public ListNode(E val,ListNode<E> next){
this.val = val;
this.next = next;
}
}
private static void print(ListNode head){
while (null != head){
System.out.print("\t" + head.val);
head = head.next;
}
}
private static boolean hasCycle(ListNode<Integer> head){
Set<ListNode> set = new HashSet<ListNode>();
while(head != null){
if(!set.add(head)){
return true;
}
head = head.next;
}
return false;
}
public static void main(String[] args) {
ListNode<Integer> list1 = new ListNode<>(1);
ListNode<Integer> list2 = new ListNode<>(3);
ListNode<Integer> list3 = new ListNode<>(5);
ListNode<Integer> list4 = new ListNode<>(7);
ListNode<Integer> list5 = new ListNode<>(9);
list1.next = list2;
list2.next = list3;
list3.next = list4;
list4.next = list5;
// 模拟存在环
list5.next = list3;
System.out.println(hasCycle(list1));
}
}
方式二:使用快慢指针的方式
private static boolean hasCycle(ListNode head){
if(null == head){
return false;
}
// 使用快慢指针进行判断
ListNode slowPtr = head, fastPtr = head;
while (slowPtr.next != null && fastPtr.next.next != null){
if(slowPtr.next == fastPtr.next.next){
return true;
}
slowPtr = slowPtr.next;
fastPtr = fastPtr.next.next;
}
return false;
}
质数的最大距离
public class Test {
public static boolean isPremium(int x) {
if (x == 1) {
return false;
}
if (x == 2) {
return true;
}
boolean flag = true; // true:是质数 false:不是质数
for (int i = 2; i < x; i++) {
if (x % i == 0) {
flag = false;
break;
}
}
return flag;
}
public static int maxDistance(int[] nums) {
// TODO 待完善边界条件
// 一个从前遍历,一个从后遍历
// 查找第一个质数
for (int i = 0; ; i++) {
if (isPremium(nums[i])) {
// 查找第二个质数
for (int j = nums.length - 1; ; j--) {
if (isPremium(nums[j])) {
return j - i;
}
}
}
}
}
public static void main(String[] args) {
// int[] nums = new int[]{4, 2, 9, 5, 3};
int[] nums = new int[]{10};
int distance = maxDistance(nums);
System.out.println(distance);
}
}
方式二:写一种给人看的
public static int maxDistance(int[] nums){
if(null == nums){
return -1;
}
int i,j;
for (i = 0; i < nums.length -1; i++) {
if(isPreim(nums[i])){
break;
}
}
for (j = nums.length - 1; j > 0; j--) {
if (isPreim(nums[j])){
break;
}
}
return j - i;
}
环形链表 II
public class Solution {
private static class ListNode<E> {
E val;
ListNode<E> next;
public ListNode(E val) {
this.val = val;
}
public ListNode(E val, ListNode<E> next) {
this.val = val;
this.next = next;
}
}
private static void print(ListNode head) {
while (null != head) {
System.out.print("\t" + head.val);
head = head.next;
}
}
private static ListNode hasCycle(ListNode head) {
if (null == head) {
return null;
}
// 使用快慢指针进行判断
// 慢指针每次只移动一个单位,快指针每次移动两个单位
ListNode slowPtr = head, fastPtr = head;
boolean isCycle = false;
while (slowPtr.next != null && fastPtr.next.next != null) {
slowPtr = slowPtr.next;
fastPtr = fastPtr.next.next;
if (slowPtr == fastPtr) {
isCycle = true;
break;
}
}
if (isCycle) {
// 如果存在环的话,就让慢指针指指回到头结点,并且快慢指针往后移动相同的单位
slowPtr = head;
while (slowPtr != fastPtr) {
slowPtr = slowPtr.next;
fastPtr = fastPtr.next;
}
return slowPtr;
}
return null;
}
public static void main(String[] args) {
ListNode<Integer> list1 = new ListNode<>(1);
ListNode<Integer> list2 = new ListNode<>(2);
ListNode<Integer> list3 = new ListNode<>(3);
ListNode<Integer> list4 = new ListNode<>(4);
ListNode<Integer> list5 = new ListNode<>(5);
list1.next = list2;
list2.next = list3;
list3.next = list4;
list4.next = list2;
ListNode node = hasCycle(list1);
System.out.println(node.val);
}
}
TODO 现在还有点理解不了,下来再好好看看 求环形链表的入环点
// 寻找环形链表的入环点
public static ListNode detectCycle(ListNode head) {
if (head == null) {
return null;
}
ListNode slow = head;
ListNode fast = head;
// 快慢指针同时移动,快指针每次移动两步
while (fast!= null && fast.next!= null) {
slow = slow.next;
fast = fast.next.next;
// 快慢指针相遇,说明存在环
if (slow == fast) {
ListNode ptr = head;
// 让一个指针从链表头开始,另一个从相遇点开始,以相同速度移动,再次相遇点就是入环点
while (ptr!= slow) {
ptr = ptr.next;
slow = slow.next;
}
return ptr;
}
}
// 不存在环
return null;
}
合并两个有序数组
方式一:使用jdk自带的工具类进行排序
public class Test {
public static int[] merge(int[] arr1, int num1, int[] arr2, int num2){
for (int i = 0; i < arr2.length; i++) {
arr1[num1 + i] = arr2[i];
}
// 利用jdk自带的工具类进行排序
Arrays.sort(arr1);
return arr1;
}
public static void main(String[] args) {
int[] nums1 = {1, 2, 3, 0, 0, 0};
int m = 3;
int[] nums2 = {2, 5, 6};
int n = 3;
int[] result = merge(nums1, m, nums2, n);
Arrays.stream(result).forEach(System.out::println);
}
}
方式二:使用双指针的方式
public class Test {
// 方式二:使用双指针进行排序
public static int[] merge(int[] nums1, int m, int[] nums2, int n) {
int k = m + n;
int[] temp = new int[k];
int num1index = 0;
int num2index = 0;
for (int index = 0; index < k; index++) {
if (num1index >= m) { // nums1数组已经取完,完全取nums2数组的值即可
temp[index] = nums2[num2index++];
}else if(num2index >= n){ // nums2数组已经取完,完全取nums1数组的值即可
temp[index] = nums1[num1index++];
}else if(nums1[num1index] < nums2[num2index]){ // nums1数组元素中的值小于nums2数组中元素的值,就取nums1数组元素中的值
temp[index] = nums1[num1index++];
}else { // nums2数组元素中的值小于nums1数组中元素的值,就取nums2数组元素中的值
temp[index] = nums2[num2index++];
}
}
// 将temp数组中的元素
for(int i = 0; i < temp.length; i++){
nums1[i] = temp[i];
}
return nums1;
}
public static void main(String[] args) {
int[] nums1 = {1, 2, 3, 0, 0, 0};
int m = 3;
int[] nums2 = {2, 5, 6};
int n = 3;
int[] result = merge(nums1, m, nums2, n);
Arrays.stream(result).forEach(System.out::println);
}
}
// TODO 还有另外一种方法,待看
判断两个链表有无相交
public class Test {
// 定义链表节点和指针
static class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
}
ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int lenA = 0, lenB = 0;
ListNode pl = headA, ps = headB;
while (pl != null) {
lenA++;
pl = pl.next;
}
while (ps != null) {
lenB++;
ps = ps.next;
}
// 因为你上面遍历了一下,所以需要重新指向
pl = headA;
ps = headB;
int len = lenA - lenB;
if (len < 0) {
pl = headB;
ps = headA;
len = lenA - lenB;
}
// 一定是pl指向的是最长的单链表
for (int i = 0; i < len; i++) {
pl = pl.next;
}
// ps 和 pl一定是在同一个起跑线上了
while (ps != pl && null != pl && null != ps) {
ps = ps.next;
pl = pl.next;
}
if(ps == pl && null != pl && null != ps){
return pl;
}
return null; // 如果没有相交就返回null
}
public static void main(String[] args) {
ListNode listNode1 = new ListNode(1);
ListNode listNode2 = new ListNode(2);
ListNode listNode3 = new ListNode(3);
ListNode listNode4 = new ListNode(4);
ListNode listNode5 = new ListNode(5);
listNode1.next = listNode2;
listNode2.next = listNode3;
listNode3.next = listNode4;
listNode4.next = listNode5;
ListNode node1 = new ListNode(1);
ListNode node2 = new ListNode(2);
ListNode node3 = new ListNode(3);
ListNode node4 = new ListNode(4);
ListNode node5 = new ListNode(5);
node1.next = node2;
node2.next = node3;
node3.next = listNode4;
// 注意两个链表相交一定是Y型的,不可能是X型的
ListNode intersectionNode = getIntersectionNode(listNode1, node1);
System.out.println(intersectionNode);
}
}
方式二:使用双指针的方式
private static ListNode getIntersectionNode(ListNode head1, ListNode head2) {
if (head1 == null || head2 == null) {
return null;
}
// 使用双指针的方式
ListNode ptr1 = head1;
ListNode ptr2 = head2;
while (ptr1 != ptr2) {
ptr1 = ptr1 == null ? head2 : ptr1.next;
ptr2 = ptr2 == null ? head1 : ptr2.next;
}
return ptr1;
}
链表中的节点每K个一组进行翻转
public class Solution {
public static class ListNode {
int val;
ListNode next;
ListNode() {
}
ListNode(int val) {
this.val = val;
}
ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
public static void print(ListNode head) {
while (head != null) {
System.out.print("\t" + head.val);
head = head.next;
}
}
public ListNode reverseKGroup(ListNode head, int k) {
// 先设置一个虚拟头结点
ListNode dummy = new ListNode(-1);
// 虚拟节点的next指针要指向head节点
dummy.next = head;
// pre表示每次要翻转的链表的头结点【上一个节点】
ListNode pre = dummy;
// end表示每次要翻转的链表的尾节点
ListNode end = dummy;
while(end.next != null){
// 通过for循环,找到每一组翻转链表的尾部
for (int i = 0; i < k && end != null; i++) {
end = end.next;
}
// 如果发现end == null,说明此时反转的额链表的节点数小于K,保持原有的顺序就可以了
if(end == null){
break;
}
ListNode next = end.next;
end.next = null;
ListNode start = pre.next;
pre.next = null;
pre.next = reverseList(start);
start.next = next;
pre = start;
end = start;
}
return dummy.next;
}
/**
* 翻转链表
* @param head
*/
// 使用递归的方式
public static ListNode reverseList(ListNode head){
if(head == null || head.next == null){
return head;
}
ListNode newhead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newhead;
}
public static void main(String[] args) {
// TODO 这个题目目前自己还不是很懂,下来再好好看看吧!!!
}
}
移动0
使用双指针 i 用来遍历数组中的每一个元素; j用来记录数组中0元素的个数
public class Solution {
public static void moveZero(int[] nums){
if(null == nums){
return;
}
// 使用双指针
int j = 0;
for (int i = 0; i < nums.length; i++) {
if(0 != nums[i]){
nums[j++] = nums[i];
}
}
for (int i = j; i < nums.length; i++) {
nums[i] = 0;
}
}
public static void main(String[] args) {
int[] arr = {0,1,0,3,12};
moveZero(arr);
Arrays.stream(arr).forEach(System.out::println);
}
}
找到数组中所有消失的元素
public class Solution {
public List<Integer> findDisappearedNumbers(int[] nums) {
for (int i = 0; i < nums.length; i++) {
while (i != nums[i] - 1 && nums[i] != nums[nums[i] - 1]) {
swap(nums, i, nums[i] - 1);
}
}
List<Integer> res = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
if(i + 1 != nums[i]){
res.add(i + 1);
}
}
return res;
}
// 两数交换
public void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
public static void main(String[] args) {
int[] nums = {4,3,2,1,8,2,3,7};
Solution solution = new Solution();
List<Integer> result = solution.findDisappearedNumbers(nums);
System.out.println(result);
}
}
// TODO 还有另外一种算法,待看
删除排序列表中的重复元素
public class Solution {
private static class ListNode<E>{
E val;
ListNode<E> next;
public ListNode(E val){
this.val = val;
}
public ListNode(E val,ListNode<E> next){
this.val = val;
this.next = next;
}
}
private static void print(ListNode head){
while (null != head){
System.out.print("\t" + head.val);
head = head.next;
}
}
private static ListNode delDuplicateNode(ListNode head){
if(null == head){
return head;
}
ListNode curr = head;
while (curr.next != null){
if(curr.val == curr.next.val){
curr.next = curr.next.next;
}else{
curr = curr.next;
}
}
return head;
}
public static void main(String[] args) {
ListNode<Integer> list1 = new ListNode<>(1);
ListNode<Integer> list2 = new ListNode<>(3);
ListNode<Integer> list3 = new ListNode<>(3);
ListNode<Integer> list4 = new ListNode<>(5);
ListNode<Integer> list5 = new ListNode<>(5);
list1.next = list2;
list2.next = list3;
list3.next = list4;
list4.next = list5;
ListNode listNode = delDuplicateNode(list1);
print(listNode);
}
}
回文链表
public class Solution {
private static class ListNode<E> {
E val;
ListNode<E> next;
public ListNode(E val) {
this.val = val;
}
public ListNode(E val, ListNode<E> next) {
this.val = val;
this.next = next;
}
}
private static void print(ListNode head) {
while (null != head) {
System.out.print("\t" + head.val);
head = head.next;
}
}
// 判断是否是回文链表 true:表示是回文链表 false:表示不是回文链表
private static boolean isPalindrome(ListNode head) {
// 使用快慢指针进行判断
ListNode slowPtr = head;
ListNode fastPtr = head;
while (slowPtr != null && fastPtr.next != null){
slowPtr = slowPtr.next;
fastPtr = fastPtr.next.next;
}
// 如果是奇数个节点
if(fastPtr != null){
slowPtr = slowPtr.next;
}
fastPtr = head; // 将快指针重新移动到链表的头部
slowPtr = reverse(slowPtr); // 将慢指针进行翻转
while (slowPtr != null){
if(slowPtr.val != fastPtr.val){
return false;
}
slowPtr = slowPtr.next;
fastPtr = fastPtr.next;
}
return true;
}
// 链表翻转
private static ListNode reverse(ListNode head){
if(head == null || head.next == null){
return head;
}
ListNode newNode = reverse(head.next);
head.next.next = head;
head.next = null;
return newNode;
}
public static void main(String[] args) {
ListNode listNode1 = new ListNode(1);
ListNode listNode2 = new ListNode(2);
ListNode listNode3 = new ListNode(3);
ListNode listNode4 = new ListNode(2);
ListNode listNode5 = new ListNode(2);
listNode1.next = listNode2;
listNode2.next = listNode3;
listNode3.next = listNode4;
listNode4.next = listNode5;
boolean flag = isPalindrome(listNode1);
System.out.println(flag);
}
}
找到链表的中间节点
public class Solution {
private static class ListNode<E> {
E val;
ListNode<E> next;
public ListNode(E val) {
this.val = val;
}
public ListNode(E val, ListNode<E> next) {
this.val = val;
this.next = next;
}
}
private static void print(ListNode head) {
while (null != head) {
System.out.print("\t" + head.val);
head = head.next;
}
}
private static ListNode findMidNode(ListNode head) {
// 使用快慢指针
ListNode slowPtr = head;
ListNode fastPtr = head;
while (slowPtr != null && fastPtr.next != null){
slowPtr = slowPtr.next;
fastPtr = fastPtr.next.next;
}
return slowPtr;
}
public static void main(String[] args) {
ListNode listNode1 = new ListNode(1);
ListNode listNode2 = new ListNode(2);
ListNode listNode3 = new ListNode(3);
ListNode listNode4 = new ListNode(2);
ListNode listNode5 = new ListNode(2);
listNode1.next = listNode2;
listNode2.next = listNode3;
listNode3.next = listNode4;
listNode4.next = listNode5;
ListNode node = findMidNode(listNode1);
System.out.println(node.val);
}
}
求环形链表中环的长度
/**
* 求环形链表中环的长度
*/
public class Solution {
private static class ListNode {
int val;
ListNode next;
public ListNode(int val) {
this.val = val;
}
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
public int cycleLength(ListNode head) {
if (null == head) {
return -1;
}
// 先判断链表有没有环
ListNode slow = head;
ListNode fast = head;
int length = 0; // 统计环的长度
while (null != slow.next && null != fast.next.next) {
slow = slow.next;
fast = fast.next.next;
// 说明链表有环
if (slow == fast) {
ListNode ptr = slow;
do {
ptr = ptr.next;
length++;
} while (ptr != slow);
break;
}
}
return length;
}
public static void main(String[] args) {
ListNode list1 = new ListNode(1);
ListNode list2 = new ListNode(3);
ListNode list3 = new ListNode(5);
ListNode list4 = new ListNode(7);
ListNode list5 = new ListNode(9);
list1.next = list2;
list2.next = list3;
list3.next = list4;
list4.next = list5;
// 模拟存在环
list5.next = list2;
Solution solution = new Solution();
System.out.println(solution.cycleLength(list1));
}
}
找到链表的倒数第K个节点
public class Solution {
private static class ListNode<E> {
E val;
ListNode<E> next;
public ListNode(E val) {
this.val = val;
}
public ListNode(E val, ListNode<E> next) {
this.val = val;
this.next = next;
}
}
private static void print(ListNode head) {
while (null != head) {
System.out.print("\t" + head.val);
head = head.next;
}
}
private static ListNode findNodeFromEnd(ListNode head,int k) {
if(head == null || k < 0){
return head;
}
// 使用快慢指针
ListNode slowPtr = head;
ListNode fastPtr = head;
// 先让快指针提前走K步
for (int i = 0; i < k; i++) {
if(fastPtr == null){
return null;
}
fastPtr = fastPtr.next;
}
// 然后快慢指针一起走
while (slowPtr != null && fastPtr != null){
slowPtr = slowPtr.next;
fastPtr = fastPtr.next;
}
return slowPtr;
}
public static void main(String[] args) {
ListNode listNode1 = new ListNode(1);
ListNode listNode2 = new ListNode(2);
ListNode listNode3 = new ListNode(3);
ListNode listNode4 = new ListNode(4);
ListNode listNode5 = new ListNode(5);
listNode1.next = listNode2;
listNode2.next = listNode3;
listNode3.next = listNode4;
listNode4.next = listNode5;
int k = 2;
ListNode node = findNodeFromEnd(listNode1, k);
System.out.println(node.val);
}
}
一个有序int数组。只有一个元素会出现一次。其他都会出现两次。找出这个int。
public class Test {
public int findSingleNum(int[] nums){
if(null == nums || nums.length == 0){
return Integer.MAX_VALUE;
}
Set<Integer> set = new HashSet<>();
for (int i = 0; i < nums.length; i++) {
if(!set.contains(nums[i])){
set.add(nums[i]);
}else {
set.remove(nums[i]);
}
}
return set.stream().findFirst().get();
}
public static void main(String[] args) {
int[] nums = {1,1,2,3,3,4,4};
Test test = new Test();
int result = test.findSingleNum(nums);
System.out.println(result);
}
}
判断某个数字是不是2的次幂
第一种方式:
/**
* 算法题:判断某个数字是不是2的幂
*/
public class Test2 {
private boolean is2power(int num) {
while (num / 2 != 0 && num % 2 == 0) {
num = num / 2;
}
return num == 1;
}
public static void main(String[] args) {
int num = 16;
Test2 test2 = new Test2();
System.out.println(test2.is2power(num));
}
}
第二种方式:
package com.example.demo02.test;
/**
* 判断一个正整数是否是2的次幂
*/
public class Solution {
/**
* 定义一个变量temp,每次通过乘以2,都来和目标值进行判断,如果相等,则是2的次幂,如果大于目标值,则不是2的次幂。
* @param num 目标值
* @return true: 是2的次幂 false:不是
*/
public boolean is2Power(int num){
// 边界条件判断
if(num == 0 || num == 1){
return false;
}
int temp = 1;
boolean is2Power;
while (true){
temp = temp * 2;
if(temp == num){
is2Power = true;
break;
}else if(temp > num){
is2Power = false;
break;
}
}
return is2Power;
}
public static void main(String[] args) {
int num = 16;
Solution solution = new Solution();
boolean power = solution.is2Power(num);
System.out.println(power);
}
}
输入一个字符串,将这个字符串中的非字母去除后,不考虑大小写的情况下判断这个字符串是否是前后对称的(AbcECba)果
/**
* 算法题:输入一个字符串,将这个字符串中的非字母去除后,不考虑大小写的情况下判断这个字符串是否是前后对称的(AbcECba)果
*/
public class Test2 {
public static boolean isMinorChar(String content) {
if (null == content || 0 == content.length()) {
return true;
}
String[] arr = content.split("");
List<String> list = Arrays.asList(arr);
List<String> modifiableList = new ArrayList<>(list);
Iterator<String> iterator = modifiableList.iterator();// 注意这里必须再包一次,否则会报错
while (iterator.hasNext()) {
String str = iterator.next();
if (!Character.isLetter(str.charAt(0))) {
iterator.remove();
}
}
// 判断是否对称
int half = list.size() / 2;
int length = list.size();
for (int i = 0; i < half; i++) {
if (!list.get(i).equalsIgnoreCase(list.get(length - i - 1))) {
return false;
}
}
return true;
}
public static void main(String[] args) {
String str = "abc5cbad";
boolean flag = isMinorChar(str);
System.out.println(flag);
}
}
字母排序
/**
* 算法题:输入一个字符串,将这个字符串中的非字母去除后,不考虑大小写的情况下判断这个字符串是否是前后对称的(AbcECba)果
*/
public class Test2 {
private final static Object OBJECT = new Object();
// 字母排序
public static String sort(String content) {
if (null == content || 0 == content.length()) {
return "";
}
String[] arr = content.split("");
TreeMap<String, Object> treeMap = new TreeMap();
for (int i = 0; i < arr.length; i++) {
treeMap.put(arr[i], OBJECT);
}
StringBuffer buffer = new StringBuffer();
treeMap.forEach((k,v) -> buffer.append(k));
return buffer.toString();
}
// 字母排序
public static String sort2(String content) {
if (null == content || 0 == content.length()) {
return "";
}
String[] arr = content.split("");
Arrays.sort(arr);
String result = Arrays.toString(arr);
return result;
}
public static void main(String[] args) {
String content = "bdca";
String result = sort2(content);
System.out.println(result);
}
}
在数组指定位置插入元素
/**
* 算法题:在数组指定位置插入元素
*/
public class Test2 {
public static void main(String[] args) {
int[] arr = new int[5];
for (int i = 0; i < 5; i++) {
arr[i] = i + 1;
}
System.out.println(Arrays.toString(arr));
int element = 10;
int index = 3;
int[] arrNew = new int[arr.length + 1]; // 需要新创建一个数组
// 分两个步骤进行
for (int i = 0; i < index; i++) {
arrNew[i] = arr[i];
}
arrNew[index] = element;
for (int i = index + 1; i < arrNew.length; i++) {
arrNew[i] = arr[i - 1];
}
System.out.println(Arrays.toString(arrNew));
}
}
字符串版本号
package com.coding;
/**
* 示例 1:
* 输入: version1 = "1.01", version2 = "1.001"
* 输出: 0
* 解释:忽略前导零,"01" 和 "001" 都表示相同的整数 "1"
* <p>
* 示例 2:
* 输入: version1 = "1.0", version2 = "1.0.0"
* 输出: 0
* 解释: version1 没有指定下标为 2 的修订号,即视为 "0"
* <p>
* 示例 3:
* 输入: version1 = "0.1", version2 = "1.1"
* 输出: -1
* 解释: version1 中下标为 0 的修订号是 "0",version2 中下标为 0 的修订号是 "1" 。0 < 1,所以 version1 < version2
*/
public class Test7 {
public static int compareVersion(String v1, String v2) {
// 将传入的两个字符串按照.进行拆分
String[] ss1 = v1.split("\\."), ss2 = v2.split("\\.");
// 得到每个拆分后数组的长度
int n = ss1.length, m = ss2.length;
// 对两个数组进行遍历,只要遍历完其中的任何一个就可以了
int i = 0, j = 0;
while (i < n || j < m) {
// 获取每一位上面的数字
int a = 0, b = 0;
if (i < n) {
a = Integer.parseInt(ss1[i++]);
}
if (j < m) {
b = Integer.parseInt(ss2[j++]);
}
// 进行判断,返回结果
if (a != b) {
return a > b ? 1 : -1;
}
}
return 0; // 表示两个字符串的版本号相等
}
public static void main(String[] args) {
String version1 = "1.01";
String version2 = "1.001";
int result = compareVersion(version1, version2);
System.out.println(result);
}
}
浙公网安备 33010602011771号