From 8bdd361686ab767cb0119986fd14c9212cb36bb5 Mon Sep 17 00:00:00 2001 From: Jason Wu Date: Thu, 25 Apr 2019 23:58:52 +0800 Subject: [PATCH 01/74] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=B8=89=E6=95=B0?= =?UTF-8?q?=E4=B9=8B=E5=92=8C=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/study/number/ThreeSum.java | 123 +++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 src/main/java/com/study/number/ThreeSum.java diff --git a/src/main/java/com/study/number/ThreeSum.java b/src/main/java/com/study/number/ThreeSum.java new file mode 100644 index 0000000..af516fe --- /dev/null +++ b/src/main/java/com/study/number/ThreeSum.java @@ -0,0 +1,123 @@ +package com.study.number; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 三数之和 + *

+ * 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。 + *

+ * 注意:答案中不可以包含重复的三元组。 + *

+ * 例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4], + *

+ * 满足要求的三元组集合为: + * [ + * [-1, 0, 1], + * [-1, -1, 2] + * ] + *

+ * https://leetcode-cn.com/problems/3sum/ + */ +public class ThreeSum { + public static void main(String[] args) { + //int[] nums = {-1, 0, 1, 2, -1, -4}; + //int[] nums = {-1, 0, 1, 0, 0}; + //int[] nums = {-1, 0, 1, 2, -1, -4}; + int[] nums = {82597, -9243, 62390, 83030, -97960, -26521, -61011, 83390, -38677, 12333, 75987, 46091, 83794, 19355, -71037, -6242, -28801, 324, 1202, -90885, -2989, -95597, -34333, 35528, 5680, 89093, -90606, 50360, -29393, -27012, 53313, 65213, 99818, -82405, -41661, -3333, -51952, 72135, -1523, 26377, 74685, 96992, 92263, 15929, 5467, -99555, -43348, -41689, -60383, -3990, 32165, 65265, -72973, -58372, 12741, -48568, -46596, 72419, -1859, 34153, 62937, 81310, -61823, -96770, -54944, 8845, -91184, 24208, -29078, 31495, 65258, 14198, 85395, 70506, -40908, 56740, -12228, -40072, 32429, 93001, 68445, -73927, 25731, -91859, -24150, 10093, -60271, -81683, -18126, 51055, 48189, -6468, 25057, 81194, -58628, 74042, 66158, -14452, -49851, -43667, 11092, 39189, -17025, -79173, 13606, 83172, 92647, -59741, 19343, -26644, -57607, 82908, -20655, 1637, 80060, 98994, 39331, -31274, -61523, 91225, -72953, 13211, -75116, -98421, -41571, -69074, 99587, 39345, 42151, -2460, 98236, 15690, -52507, -95803, -48935, -46492, -45606, -79254, -99851, 52533, 73486, 39948, -7240, 71815, -585, -96252, 90990, -93815, 93340, -71848, 58733, -14859, -83082, -75794, -82082, -24871, -15206, 91207, -56469, -93618, 67131, -8682, 75719, 87429, -98757, -7535, -24890, -94160, 85003, 33928, 75538, 97456, -66424, -60074, -8527, -28697, -22308, 2246, -70134, -82319, -10184, 87081, -34949, -28645, -47352, -83966, -60418, -15293, -53067, -25921, 55172, 75064, 95859, 48049, 34311, -86931, -38586, 33686, -36714, 96922, 76713, -22165, -80585, -34503, -44516, 39217, -28457, 47227, -94036, 43457, 24626, -87359, 26898, -70819, 30528, -32397, -69486, 84912, -1187, -98986, -32958, 4280, -79129, -65604, 9344, 58964, 50584, 71128, -55480, 24986, 15086, -62360, -42977, -49482, -77256, -36895, -74818, 20, 3063, -49426, 28152, -97329, 6086, 86035, -88743, 35241, 44249, 19927, -10660, 89404, 24179, -26621, -6511, 57745, -28750, 96340, -97160, -97822, -49979, 52307, 79462, 94273, -24808, 77104, 9255, -83057, 77655, 21361, 55956, -9096, 48599, -40490, -55107, 2689, 29608, 20497, 66834, -34678, 23553, -81400, -66630, -96321, -34499, -12957, -20564, 25610, -4322, -58462, 20801, 53700, 71527, 24669, -54534, 57879, -3221, 33636, 3900, 97832, -27688, -98715, 5992, 24520, -55401, -57613, -69926, 57377, -77610, 20123, 52174, 860, 60429, -91994, -62403, -6218, -90610, -37263, -15052, 62069, -96465, 44254, 89892, -3406, 19121, -41842, -87783, -64125, -56120, 73904, -22797, -58118, -4866, 5356, 75318, 46119, 21276, -19246, -9241, -97425, 57333, -15802, 93149, 25689, -5532, 95716, 39209, -87672, -29470, -16324, -15331, 27632, -39454, 56530, -16000, 29853, 46475, 78242, -46602, 83192, -73440, -15816, 50964, -36601, 89758, 38375, -40007, -36675, -94030, 67576, 46811, -64919, 45595, 76530, 40398, 35845, 41791, 67697, -30439, -82944, 63115, 33447, -36046, -50122, -34789, 43003, -78947, -38763, -89210, 32756, -20389, -31358, -90526, -81607, 88741, 86643, 98422, 47389, -75189, 13091, 95993, -15501, 94260, -25584, -1483, -67261, -70753, 25160, 89614, -90620, -48542, 83889, -12388, -9642, -37043, -67663, 28794, -8801, 13621, 12241, 55379, 84290, 21692, -95906, -85617, -17341, -63767, 80183, -4942, -51478, 30997, -13658, 8838, 17452, -82869, -39897, 68449, 31964, 98158, -49489, 62283, -62209, -92792, -59342, 55146, -38533, 20496, 62667, 62593, 36095, -12470, 5453, -50451, 74716, -17902, 3302, -16760, -71642, -34819, 96459, -72860, 21638, 47342, -69897, -40180, 44466, 76496, 84659, 13848, -91600, -90887, -63742, -2156, -84981, -99280, 94326, -33854, 92029, -50811, 98711, -36459, -75555, 79110, -88164, -97397, -84217, 97457, 64387, 30513, -53190, -83215, 252, 2344, -27177, -92945, -89010, 82662, -11670, 86069, 53417, 42702, 97082, 3695, -14530, -46334, 17910, 77999, 28009, -12374, 15498, -46941, 97088, -35030, 95040, 92095, -59469, -24761, 46491, 67357, -66658, 37446, -65130, -50416, 99197, 30925, 27308, 54122, -44719, 12582, -99525, -38446, -69050, -22352, 94757, -56062, 33684, -40199, -46399, 96842, -50881, -22380, -65021, 40582, 53623, -76034, 77018, -97074, -84838, -22953, -74205, 79715, -33920, -35794, -91369, 73421, -82492, 63680, -14915, -33295, 37145, 76852, -69442, 60125, -74166, 74308, -1900, -30195, -16267, -60781, -27760, 5852, 38917, 25742, -3765, 49097, -63541, 98612, -92865, -30248, 9612, -8798, 53262, 95781, -42278, -36529, 7252, -27394, -5021, 59178, 80934, -48480, -75131, -54439, -19145, -48140, 98457, -6601, -51616, -89730, 78028, 32083, -48904, 16822, -81153, -8832, 48720, -80728, -45133, -86647, -4259, -40453, 2590, 28613, 50523, -4105, -27790, -74579, -17223, 63721, 33489, -47921, 97628, -97691, -14782, -65644, 18008, -93651, -71266, 80990, -76732, -47104, 35368, 28632, 59818, -86269, -89753, 34557, -92230, -5933, -3487, -73557, -13174, -43981, -43630, -55171, 30254, -83710, -99583, -13500, 71787, 5017, -25117, -78586, 86941, -3251, -23867, -36315, 75973, 86272, -45575, 77462, -98836, -10859, 70168, -32971, -38739, -12761, 93410, 14014, -30706, -77356, -85965, -62316, 63918, -59914, -64088, 1591, -10957, 38004, 15129, -83602, -51791, 34381, -89382, -26056, 8942, 5465, 71458, -73805, -87445, -19921, -80784, 69150, -34168, 28301, -68955, 18041, 6059, 82342, 9947, 39795, 44047, -57313, 48569, 81936, -2863, -80932, 32976, -86454, -84207, 33033, 32867, 9104, -16580, -25727, 80157, -70169, 53741, 86522, 84651, 68480, 84018, 61932, 7332, -61322, -69663, 76370, 41206, 12326, -34689, 17016, 82975, -23386, 39417, 72793, 44774, -96259, 3213, 79952, 29265, -61492, -49337, 14162, 65886, 3342, -41622, -62659, -90402, -24751, 88511, 54739, -21383, -40161, -96610, -24944, -602, -76842, -21856, 69964, 43994, -15121, -85530, 12718, 13170, -13547, 69222, 62417, -75305, -81446, -38786, -52075, -23110, 97681, -82800, -53178, 11474, 35857, 94197, -58148, -23689, 32506, 92154, -64536, -73930, -77138, 97446, -83459, 70963, 22452, 68472, -3728, -25059, -49405, 95129, -6167, 12808, 99918, 30113, -12641, -26665, 86362, -33505, 50661, 26714, 33701, 89012, -91540, 40517, -12716, -57185, -87230, 29914, -59560, 13200, -72723, 58272, 23913, -45586, -96593, -26265, -2141, 31087, 81399, 92511, -34049, 20577, 2803, 26003, 8940, 42117, 40887, -82715, 38269, 40969, -50022, 72088, 21291, -67280, -16523, 90535, 18669, 94342, -39568, -88080, -99486, -20716, 23108, -28037, 63342, 36863, -29420, -44016, 75135, 73415, 16059, -4899, 86893, 43136, -7041, 33483, -67612, 25327, 40830, 6184, 61805, 4247, 81119, -22854, -26104, -63466, 63093, -63685, 60369, 51023, 51644, -16350, 74438, -83514, 99083, 10079, -58451, -79621, 48471, 67131, -86940, 99093, 11855, -22272, -67683, -44371, 9541, 18123, 37766, -70922, 80385, -57513, -76021, -47890, 36154, 72935, 84387, -92681, -88303, -7810, 59902, -90, -64704, -28396, -66403, 8860, 13343, 33882, 85680, 7228, 28160, -14003, 54369, -58893, 92606, -63492, -10101, 64714, 58486, 29948, -44679, -22763, 10151, -56695, 4031, -18242, -36232, 86168, -14263, 9883, 47124, 47271, 92761, -24958, -73263, -79661, -69147, -18874, 29546, -92588, -85771, 26451, -86650, -43306, -59094, -47492, -34821, -91763, -47670, 33537, 22843, 67417, -759, 92159, 63075, 94065, -26988, 55276, 65903, 30414, -67129, -99508, -83092, -91493, -50426, 14349, -83216, -76090, 32742, -5306, -93310, -60750, -60620, -45484, -21108, -58341, -28048, -52803, 69735, 78906, 81649, 32565, -86804, -83202, -65688, -1760, 89707, 93322, -72750, 84134, 71900, -37720, 19450, -78018, 22001, -23604, 26276, -21498, 65892, -72117, -89834, -23867, 55817, -77963, 42518, 93123, -83916, 63260, -2243, -97108, 85442, -36775, 17984, -58810, 99664, -19082, 93075, -69329, 87061, 79713, 16296, 70996, 13483, -74582, 49900, -27669, -40562, 1209, -20572, 34660, 83193, 75579, 7344, 64925, 88361, 60969, 3114, 44611, -27445, 53049, -16085, -92851, -53306, 13859, -33532, 86622, -75666, -18159, -98256, 51875, -42251, -27977, -18080, 23772, 38160, 41779, 9147, 94175, 99905, -85755, 62535, -88412, -52038, -68171, 93255, -44684, -11242, -104, 31796, 62346, -54931, -55790, -70032, 46221, 56541, -91947, 90592, 93503, 4071, 20646, 4856, -63598, 15396, -50708, 32138, -85164, 38528, -89959, 53852, 57915, -42421, -88916, -75072, 67030, -29066, 49542, -71591, 61708, -53985, -43051, 28483, 46991, -83216, 80991, -46254, -48716, 39356, -8270, -47763, -34410, 874, -1186, -7049, 28846, 11276, 21960, -13304, -11433, -4913, 55754, 79616, 70423, -27523, 64803, 49277, 14906, -97401, -92390, 91075, 70736, 21971, -3303, 55333, -93996, 76538, 54603, -75899, 98801, 46887, 35041, 48302, -52318, 55439, 24574, 14079, -24889, 83440, 14961, 34312, -89260, -22293, -81271, -2586, -71059, -10640, -93095, -5453, -70041, 66543, 74012, -11662, -52477, -37597, -70919, 92971, -17452, -67306, -80418, 7225, -89296, 24296, 86547, 37154, -10696, 74436, -63959, 58860, 33590, -88925, -97814, -83664, 85484, -8385, -50879, 57729, -74728, -87852, -15524, -91120, 22062, 28134, 80917, 32026, 49707, -54252, -44319, -35139, 13777, 44660, 85274, 25043, 58781, -89035, -76274, 6364, -63625, 72855, 43242, -35033, 12820, -27460, 77372, -47578, -61162, -70758, -1343, -4159, 64935, 56024, -2151, 43770, 19758, -30186, -86040, 24666, -62332, -67542, 73180, -25821, -27826, -45504, -36858, -12041, 20017, -24066, -56625, -52097, -47239, -90694, 8959, 7712, -14258, -5860, 55349, 61808, -4423, -93703, 64681, -98641, -25222, 46999, -83831, -54714, 19997, -68477, 66073, 51801, -66491, 52061, -52866, 79907, -39736, -68331, 68937, 91464, 98892, 910, 93501, 31295, -85873, 27036, -57340, 50412, 21, -2445, 29471, 71317, 82093, -94823, -54458, -97410, 39560, -7628, 66452, 39701, 54029, 37906, 46773, 58296, 60370, -61090, 85501, -86874, 71443, -72702, -72047, 14848, 34102, 77975, -66294, -36576, 31349, 52493, -70833, -80287, 94435, 39745, -98291, 84524, -18942, 10236, 93448, 50846, 94023, -6939, 47999, 14740, 30165, 81048, 84935, -19177, -13594, 32289, 62628, -90612, -542, -66627, 64255, 71199, -83841, -82943, -73885, 8623, -67214, -9474, -35249, 62254, -14087, -90969, 21515, -83303, 94377, -91619, 19956, -98810, 96727, -91939, 29119, -85473, -82153, -69008, 44850, 74299, -76459, -86464, 8315, -49912, -28665, 59052, -69708, 76024, -92738, 50098, 18683, -91438, 18096, -19335, 35659, 91826, 15779, -73070, 67873, -12458, -71440, -46721, 54856, 97212, -81875, 35805, 36952, 68498, 81627, -34231, 81712, 27100, -9741, -82612, 18766, -36392, 2759, 41728, 69743, 26825, 48355, -17790, 17165, 56558, 3295, -24375, 55669, -16109, 24079, 73414, 48990, -11931, -78214, 90745, 19878, 35673, -15317, -89086, 94675, -92513, 88410, -93248, -19475, -74041, -19165, 32329, -26266, -46828, -18747, 45328, 8990, -78219, -25874, -74801, -44956, -54577, -29756, -99822, -35731, -18348, -68915, -83518, -53451, 95471, -2954, -13706, -8763, -21642, -37210, 16814, -60070, -42743, 27697, -36333, -42362, 11576, 85742, -82536, 68767, -56103, -63012, 71396, -78464, -68101, -15917, -11113, -3596, 77626, -60191, -30585, -73584, 6214, -84303, 18403, 23618, -15619, -89755, -59515, -59103, -74308, -63725, -29364, -52376, -96130, 70894, -12609, 50845, -2314, 42264, -70825, 64481, 55752, 4460, -68603, -88701, 4713, -50441, -51333, -77907, 97412, -66616, -49430, 60489, -85262, -97621, -18980, 44727, -69321, -57730, 66287, -92566, -64427, -14270, 11515, -92612, -87645, 61557, 24197, -81923, -39831, -10301, -23640, -76219, -68025, 92761, -76493, 68554, -77734, -95620, -11753, -51700, 98234, -68544, -61838, 29467, 46603, -18221, -35441, 74537, 40327, -58293, 75755, -57301, -7532, -94163, 18179, -14388, -22258, -46417, -48285, 18242, -77551, 82620, 250, -20060, -79568, -77259, 82052, -98897, -75464, 48773, -79040, -11293, 45941, -67876, -69204, -46477, -46107, 792, 60546, -34573, -12879, -94562, 20356, -48004, -62429, 96242, 40594, 2099, 99494, 25724, -39394, -2388, -18563, -56510, -83570, -29214, 3015, 74454, 74197, 76678, -46597, 60630, -76093, 37578, -82045, -24077, 62082, -87787, -74936, 58687, 12200, -98952, 70155, -77370, 21710, -84625, -60556, -84128, 925, 65474, -15741, -94619, 88377, 89334, 44749, 22002, -45750, -93081, -14600, -83447, 46691, 85040, -66447, -80085, 56308, 44310, 24979, -29694, 57991, 4675, -71273, -44508, 13615, -54710, 23552, -78253, -34637, 50497, 68706, 81543, -88408, -21405, 6001, -33834, -21570, -46692, -25344, 20310, 71258, -97680, 11721, 59977, 59247, -48949, 98955, -50276, -80844, -27935, -76102, 55858, -33492, 40680, 66691, -33188, 8284, 64893, -7528, 6019, -85523, 8434, -64366, -56663, 26862, 30008, -7611, -12179, -70076, 21426, -11261, -36864, -61937, -59677, 929, -21052, 3848, -20888, -16065, 98995, -32293, -86121, -54564, 77831, 68602, 74977, 31658, 40699, 29755, 98424, 80358, -69337, 26339, 13213, -46016, -18331, 64713, -46883, -58451, -70024, -92393, -4088, 70628, -51185, 71164, -75791, -1636, -29102, -16929, -87650, -84589, -24229, -42137, -15653, 94825, 13042, 88499, -47100, -90358, -7180, 29754, -65727, -42659, -85560, -9037, -52459, 20997, -47425, 17318, 21122, 20472, -23037, 65216, -63625, -7877, -91907, 24100, -72516, 22903, -85247, -8938, 73878, 54953, 87480, -31466, -99524, 35369, -78376, 89984, -15982, 94045, -7269, 23319, -80456, -37653, -76756, 2909, 81936, 54958, -12393, 60560, -84664, -82413, 66941, -26573, -97532, 64460, 18593, -85789, -38820, -92575, -43663, -89435, 83272, -50585, 13616, -71541, -53156, 727, -27644, 16538, 34049, 57745, 34348, 35009, 16634, -18791, 23271, -63844, 95817, 21781, 16590, 59669, 15966, -6864, 48050, -36143, 97427, -59390, 96931, 78939, -1958, 50777, 43338, -51149, 39235, -27054, -43492, 67457, -83616, 37179, 10390, 85818, 2391, 73635, 87579, -49127, -81264, -79023, -81590, 53554, -74972, -83940, -13726, -39095, 29174, 78072, 76104, 47778, 25797, -29515, -6493, -92793, 22481, -36197, -65560, 42342, 15750, 97556, 99634, -56048, -35688, 13501, 63969, -74291, 50911, 39225, 93702, -3490, -59461, -30105, -46761, -80113, 92906, -68487, 50742, 36152, -90240, -83631, 24597, -50566, -15477, 18470, 77038, 40223, -80364, -98676, 70957, -63647, 99537, 13041, 31679, 86631, 37633, -16866, 13686, -71565, 21652, -46053, -80578, -61382, 68487, -6417, 4656, 20811, 67013, -30868, -11219, 46, 74944, 14627, 56965, 42275, -52480, 52162, -84883, -52579, -90331, 92792, 42184, -73422, -58440, 65308, -25069, 5475, -57996, 59557, -17561, 2826, -56939, 14996, -94855, -53707, 99159, 43645, -67719, -1331, 21412, 41704, 31612, 32622, 1919, -69333, -69828, 22422, -78842, 57896, -17363, 27979, -76897, 35008, 46482, -75289, 65799, 20057, 7170, 41326, -76069, 90840, -81253, -50749, 3649, -42315, 45238, -33924, 62101, 96906, 58884, -7617, -28689, -66578, 62458, 50876, -57553, 6739, 41014, -64040, -34916, 37940, 13048, -97478, -11318, -89440, -31933, -40357, -59737, -76718, -14104, -31774, 28001, 4103, 41702, -25120, -31654, 63085, -3642, 84870, -83896, -76422, -61520, 12900, 88678, 85547, 33132, -88627, 52820, 63915, -27472, 78867, -51439, 33005, -23447, -3271, -39308, 39726, -74260, -31874, -36893, 93656, 910, -98362, 60450, -88048, 99308, 13947, 83996, -90415, -35117, 70858, -55332, -31721, 97528, 82982, -86218, 6822, 25227, 36946, 97077, -4257, -41526, 56795, 89870, 75860, -70802, 21779, 14184, -16511, -89156, -31422, 71470, 69600, -78498, 74079, -19410, 40311, 28501, 26397, -67574, -32518, 68510, 38615, 19355, -6088, -97159, -29255, -92523, 3023, -42536, -88681, 64255, 41206, 44119, 52208, 39522, -52108, 91276, -70514, 83436, 63289, -79741, 9623, 99559, 12642, 85950, 83735, -21156, -67208, 98088, -7341, -27763, -30048, -44099, -14866, -45504, -91704, 19369, 13700, 10481, -49344, -85686, 33994, 19672, 36028, 60842, 66564, -24919, 33950, -93616, -47430, -35391, -28279, 56806, 74690, 39284, -96683, -7642, -75232, 37657, -14531, -86870, -9274, -26173, 98640, 88652, 64257, 46457, 37814, -19370, 9337, -22556, -41525, 39105, -28719, 51611, -93252, 98044, -90996, 21710, -47605, -64259, -32727, 53611, -31918, -3555, 33316, -66472, 21274, -37731, -2919, 15016, 48779, -88868, 1897, 41728, 46344, -89667, 37848, 68092, -44011, 85354, -43776, 38739, -31423, -66330, 65167, -22016, 59405, 34328, -60042, 87660, -67698, -59174, -1408, -46809, -43485, -88807, -60489, 13974, 22319, 55836, -62995, -37375, -4185, 32687, -36551, -75237, 58280, 26942, -73756, 71756, 78775, -40573, 14367, -71622, -77338, 24112, 23414, -7679, -51721, 87492, 85066, -21612, 57045, 10673, -96836, 52461, -62218, -9310, 65862, -22748, 89906, -96987, -98698, 26956, -43428, 46141, 47456, 28095, 55952, 67323, -36455, -60202, -43302, -82932, 42020, 77036, 10142, 60406, 70331, 63836, 58850, -66752, 52109, 21395, -10238, -98647, -41962, 27778, 69060, 98535, -28680, -52263, -56679, 66103, -42426, 27203, 80021, 10153, 58678, 36398, 63112, 34911, 20515, 62082, -15659, -40785, 27054, 43767, -20289, 65838, -6954, -60228, -72226, 52236, -35464, 25209, -15462, -79617, -41668, -84083, 62404, -69062, 18913, 46545, 20757, 13805, 24717, -18461, -47009, -25779, 68834, 64824, 34473, 39576, 31570, 14861, -15114, -41233, 95509, 68232, 67846, 84902, -83060, 17642, -18422, 73688, 77671, -26930, 64484, -99637, 73875, 6428, 21034, -73471, 19664, -68031, 15922, -27028, 48137, 54955, -82793, -41144, -10218, -24921, -28299, -2288, 68518, -54452, 15686, -41814, 66165, -72207, -61986, 80020, 50544, -99500, 16244, 78998, 40989, 14525, -56061, -24692, -94790, 21111, 37296, -90794, 72100, 70550, -31757, 17708, -74290, 61910, 78039, -78629, -25033, 73172, -91953, 10052, 64502, 99585, -1741, 90324, -73723, 68942, 28149, 30218, 24422, 16659, 10710, -62594, 94249, 96588, 46192, 34251, 73500, -65995, -81168, 41412, -98724, -63710, -54696, -52407, 19746, 45869, 27821, -94866, -76705, -13417, -61995, -71560, 43450, 67384, -8838, -80293, -28937, 23330, -89694, -40586, 46918, 80429, -5475, 78013, 25309, -34162, 37236, -77577, 86744, 26281, -29033, -91813, 35347, 13033, -13631, -24459, 3325, -71078, -75359, 81311, 19700, 47678, -74680, -84113, 45192, 35502, 37675, 19553, 76522, -51098, -18211, 89717, 4508, -82946, 27749, 85995, 89912, -53678, -64727, -14778, 32075, -63412, -40524, 86440, -2707, -36821, 63850, -30883, 67294, -99468, -23708, 34932, 34386, 98899, 29239, -23385, 5897, 54882, 98660, 49098, 70275, 17718, 88533, 52161, 63340, 50061, -89457, 19491, -99156, 24873, -17008, 64610, -55543, 50495, 17056, -10400, -56678, -29073, -42960, -76418, 98562, -88104, -96255, 10159, -90724, 54011, 12052, 45871, -90933, -69420, 67039, 37202, 78051, -52197, -40278, -58425, 65414, -23394, -1415, 6912, -53447, 7352, 17307, -78147, 63727, 98905, 55412, -57658, -32884, -44878, 22755, 39730, 3638, 35111, 39777, 74193, 38736, -11829, -61188, -92757, 55946, -71232, -63032, -83947, 39147, -96684, -99233, 25131, -32197, 24406, -55428, -61941, 25874, -69453, 64483, -19644, -68441, 12783, 87338, -48676, 66451, -447, -61590, 50932, -11270, 29035, 65698, -63544, 10029, 80499, -9461, 86368, 91365, -81810, -71914, -52056, -13782, 44240, -30093, -2437, 24007, 67581, -17365, -69164, -8420, -69289, -29370, 48010, 90439, 13141, 69243, 50668, 39328, 61731, 78266, -81313, 17921, -38196, 55261, 9948, -24970, 75712, -72106, 28696, 7461, 31621, 61047, 51476, 56512, 11839, -96916, -82739, 28924, -99927, 58449, 37280, 69357, 11219, -32119, -62050, -48745, -83486, -52376, 42668, 82659, 68882, 38773, 46269, -96005, 97630, 25009, -2951, -67811, 99801, 81587, -79793, -18547, -83086, 69512, 33127, -92145, -88497, 47703, 59527, 1909, 88785, -88882, 69188, -46131, -5589, -15086, 36255, -53238, -33009, 82664, 53901, 35939, -42946, -25571, 33298, 69291, 53199, 74746, -40127, -39050, 91033, 51717, -98048, 87240, 36172, 65453, -94425, -63694, -30027, 59004, 88660, 3649, -20267, -52565, -67321, 34037, 4320, 91515, -56753, 60115, 27134, 68617, -61395, -26503, -98929, -8849, -63318, 10709, -16151, 61905, -95785, 5262, 23670, -25277, 90206, -19391, 45735, 37208, -31992, -92450, 18516, -90452, -58870, -58602, 93383, 14333, 17994, 82411, -54126, -32576, 35440, -60526, -78764, -25069, -9022, -394, 92186, -38057, 55328, -61569, 67780, 77169, 19546, -92664, -94948, 44484, -13439, 83529, 27518, -48333, 72998, 38342, -90553, -98578, -76906, 81515, -16464, 78439, 92529, 35225, -39968, -10130, -7845, -32245, -74955, -74996, 67731, -13897, -82493, 33407, 93619, 59560, -24404, -57553, 19486, -45341, 34098, -24978, -33612, 79058, 71847, 76713, -95422, 6421, -96075, -59130, -28976, -16922, -62203, 69970, 68331, 21874, 40551, 89650, 51908, 58181, 66480, -68177, 34323, -3046, -49656, -59758, 43564, -10960, -30796, 15473, -20216, 46085, -85355, 41515, -30669, -87498, 57711, 56067, 63199, -83805, 62042, 91213, -14606, 4394, -562, 74913, 10406, 96810, -61595, 32564, 31640, -9732, 42058, 98052, -7908, -72330, 1558, -80301, 34878, 32900, 3939, -8824, 88316, 20937, 21566, -3218, -66080, -31620, 86859, 54289, 90476, -42889, -15016, -18838, 75456, 30159, -67101, 42328, -92703, 85850, -5475, 23470, -80806, 68206, 17764, 88235, 46421, -41578, 74005, -81142, 80545, 20868, -1560, 64017, 83784, 68863, -97516, -13016, -72223, 79630, -55692, 82255, 88467, 28007, -34686, -69049, -41677, 88535, -8217, 68060, -51280, 28971, 49088, 49235, 26905, -81117, -44888, 40623, 74337, -24662, 97476, 79542, -72082, -35093, 98175, -61761, -68169, 59697, -62542, -72965, 59883, -64026, -37656, -92392, -12113, -73495, 98258, 68379, -21545, 64607, -70957, -92254, -97460, -63436, -8853, -19357, -51965, -76582, 12687, -49712, 45413, -60043, 33496, 31539, -57347, 41837, 67280, -68813, 52088, -13155, -86430, -15239, -45030, 96041, 18749, -23992, 46048, 35243, -79450, 85425, -58524, 88781, -39454, 53073, -48864, -82289, 39086, 82540, -11555, 25014, -5431, -39585, -89526, 2705, 31953, -81611, 36985, -56022, 68684, -27101, 11422, 64655, -26965, -63081, -13840, -91003, -78147, -8966, 41488, 1988, 99021, -61575, -47060, 65260, -23844, -21781, -91865, -19607, 44808, 2890, 63692, -88663, -58272, 15970, -65195, -45416, -48444, -78226, -65332, -24568, 42833, -1806, -71595, 80002, -52250, 30952, 48452, -90106, 31015, -22073, 62339, 63318, 78391, 28699, 77900, -4026, -76870, -45943, 33665, 9174, -84360, -22684, -16832, -67949, -38077, -38987, -32847, 51443, -53580, -13505, 9344, -92337, 26585, 70458, -52764, -67471, -68411, -1119, -2072, -93476, 67981, 40887, -89304, -12235, 41488, 1454, 5355, -34855, -72080, 24514, -58305, 3340, 34331, 8731, 77451, -64983, -57876, 82874, 62481, -32754, -39902, 22451, -79095, -23904, 78409, -7418, 77916}; + List> resultList = threeSum(nums); + //List> resultList = threeSum2(nums); + + for (List list : resultList) { + for (Integer num : list) { + System.out.print(num + " "); + } + System.out.println(); + } + } + + private static List> threeSum(int[] nums) { + Arrays.sort(nums); + List> ls = new ArrayList>(); + + for (int i = 0; i < nums.length - 2; i++) { + if (i == 0 || (i > 0 && nums[i] != nums[i - 1])) { // 跳过可能重复的答案 + + int l = i + 1, r = nums.length - 1, sum = 0 - nums[i]; + while (l < r) { + if (nums[l] + nums[r] == sum) { + ls.add(Arrays.asList(nums[i], nums[l], nums[r])); + while (l < r && nums[l] == nums[l + 1]) l++; + while (l < r && nums[r] == nums[r - 1]) r--; + l++; + r--; + } else if (nums[l] + nums[r] < sum) { + while (l < r && nums[l] == nums[l + 1]) l++; // 跳过重复值 + l++; + } else { + while (l < r && nums[r] == nums[r - 1]) r--; + r--; + } + } + } + } + return ls; + } + + + /** + * 穷举法 (不推荐) + *

+ * 时间复杂度O^3 + * + * @param nums + * @return + */ + private static List> threeSum2(int[] nums) { + List> resultList = new ArrayList>(); + for (int i = 0; i < nums.length; i++) { + for (int j = i + 1; j < nums.length; j++) { + for (int z = j + 1; z < nums.length; z++) { + if (nums[i] + nums[j] + nums[z] == 0) { + if (i != j && i != z && j != z) { + //去掉可能重复的值 这里需要注意相同值的处理 比如 0 0 0 + if (!contains(resultList, nums[i], nums[j], nums[z])) + resultList.add(Arrays.asList(nums[i], nums[j], nums[z])); + } + } + } + } + } + return resultList; + } + + /** + * 判断resultList是否有一条集合包含a,b,c3个元素 + * + * @param resultList + * @param a + * @param b + * @param c + * @return + */ + private static boolean contains(List> resultList, int a, int b, int c) { + for (List result : resultList) { + // 首先判断resultList某一个子集合中是否有元素与a,b,c相等 比如子集合{0,1,-1} 中有元素0和 {0,0,0}相等 + boolean contains = result.contains(a) && result.contains(b) && result.contains(c); + // 再判断这个子集合中是否有元素与a,b,c均不相等 比如 子集合{0,1,-1}中有1和-1 与 {0,0,0}中的元素均不相等 + + if (!contains) continue; + + for (Integer r : result) { + if (r != a && r != b && r != c) { + contains = false; //说明子集合有元素与{0,0,0}中的元素不相等, 那么子集合元素和a,b,c并不完全相同 + } + } + + if (contains) return true; + } + return false; + } +} From 95c68c90855ea1233c6c4736f612145ecfc125a5 Mon Sep 17 00:00:00 2001 From: Jason Wu Date: Thu, 19 Dec 2019 17:28:23 +0800 Subject: [PATCH 02/74] =?UTF-8?q?=E5=90=88=E5=B9=B6=E4=B8=A4=E4=B8=AA?= =?UTF-8?q?=E5=8D=87=E5=BA=8F=E6=95=B0=E7=BB=84=EF=BC=88=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E6=96=B0=E5=BB=BA=E6=95=B0=E7=BB=84=E7=9A=84=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/array/MergeTwoASCSortedArray.java | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 src/main/java/com/study/array/MergeTwoASCSortedArray.java diff --git a/src/main/java/com/study/array/MergeTwoASCSortedArray.java b/src/main/java/com/study/array/MergeTwoASCSortedArray.java new file mode 100644 index 0000000..c10a2fc --- /dev/null +++ b/src/main/java/com/study/array/MergeTwoASCSortedArray.java @@ -0,0 +1,133 @@ +package com.study.array; + +/** + * 合并两个升序数组 + *

+ * 可以使用如下方法: + * 1.新数组存两个数组参数的方法,两个数组不变 + */ +public class MergeTwoASCSortedArray { + public static void main(String[] args) { + int[] num1 = {1, 2, 5, 7, 9, 10}; + int[] num2 = {1, 4, 6, 8}; + +// int[] newNum = merge(num1, num2); +// int[] newNum = merge2(num1, num2); + int[] newNum = merge3(num1, num2); + + for (int i = 0; i < newNum.length; i++) { + System.out.print(newNum[i] + " "); + } + } + + /** + * 新建一个数组来存取两个数组的内容 + * 多次迭代完成 + * + * @param num1 + * @param num2 + * @return + */ + private static int[] merge(int[] num1, int[] num2) { + int a = 0, b = 0, c = 0; + + int[] newNum = new int[num1.length + num2.length]; + + // 在数组num1和num2都不超出索引的情况下遍历,一旦num1或者num2 其中一个数组遍历完就退出循环,否则将出现索引越界的情况。 + // 假设 num2遍历完了, b的长度等于num2,这个时候需要退出循环,否则里面执行num2【b】的时候会报索引越界 + while (a < num1.length && b < num2.length) { + // 将小的元素放到前面 + if (num1[a] < num2[b]) + newNum[c++] = num1[a++]; //先放元素 再各自索引+1 + else + newNum[c++] = num2[b++]; + } + + // 元素补齐 + // 执行到这里 num1或num2可能会有元素没有加到newNum中,需要再补充。 + // 如果num1中还有元素未添加的 继续添加 + while (a < num1.length) { + newNum[c++] = num1[a++]; + } + + // 如果num2中还有元素未添加的继续添加 + while (b < num2.length) { + newNum[c++] = num2[b++]; + } + return newNum; + } + + /** + * 新建一个数组来存取两个数组的内容 + * 多次迭代完成 + * + * @param num1 + * @param num2 + * @return + */ + private static int[] merge2(int[] num1, int[] num2) { + int i = 0, j = 0, k = 0; + + int[] newNum = new int[num1.length + num2.length]; + + // 一旦有一个数组的元素全部取出,就退出循环,避免数组越界的情况发生 + while (i < num1.length && j < num2.length) { + // 两个数组中的元素做比较, 将小的元素放前面 + if (num1[i] < num2[j]) + newNum[k++] = num1[i++]; + else + newNum[k++] = num2[j++]; + } + + // 将剩下的某个未取出元素的数组 再取出元素 + // 通常只剩下一个数组,因为上面的循环执行完 肯定是另一个数组已取完 + while (i < num1.length) { + newNum[k++] = num1[i++]; + } + + while (j < num2.length) { + newNum[k++] = num2[j++]; + } + + return newNum; + } + + /** + * 新建一个数组来存取两个数组的内容 + * 1次迭代完成 + * + * @param num1 + * @param num2 + * @return + */ + private static int[] merge3(int[] num1, int[] num2) { + int i = 0, j = 0, k = 0; + + int[] newNum = new int[num1.length + num2.length]; + + // 一次循环完成,需要处理数组越界的问题 + while (i < num1.length || j < num2.length) { + + // 如果数组num1越界,说明已取完,那就取另一个数组num2, 需要确保num2没越界 + if (i >= num1.length && j < num2.length) { + newNum[k++] = num2[j++]; + continue; + } + + // 如果数组num2越界,说明已取完,那就取另一个数组num1,需要确保num1没越界 + if (j >= num2.length && i < num1.length) { + newNum[k++] = num1[i++]; + continue; + } + + // 两个数组中的元素做比较, 将小的元素放前面 + if (num1[i] < num2[j]) + newNum[k++] = num1[i++]; + else + newNum[k++] = num2[j++]; + + } + + return newNum; + } +} From fbd60c6444928f49f5f35d8d702af05303369948 Mon Sep 17 00:00:00 2001 From: Jason Wu Date: Sun, 5 Jan 2020 11:43:03 +0800 Subject: [PATCH 03/74] =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91=E5=B1=82?= =?UTF-8?q?=E7=BA=A7=E9=81=8D=E5=8E=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/study/binarytree/LevelTraversal.java | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 src/main/java/com/study/binarytree/LevelTraversal.java diff --git a/src/main/java/com/study/binarytree/LevelTraversal.java b/src/main/java/com/study/binarytree/LevelTraversal.java new file mode 100644 index 0000000..0b3a048 --- /dev/null +++ b/src/main/java/com/study/binarytree/LevelTraversal.java @@ -0,0 +1,125 @@ +package com.study.binarytree; + +import com.study.utils.TreeUtils; + +import java.util.*; + +/** + * 层级遍历二叉树,广度优先遍历bfs + * 1 + * 2 3 + * 4 5 6 7 + * https://leetcode-cn.com/problems/binary-tree-level-order-traversal/comments/ + *

+ * 解题思路, + * 1.借用一个queue先进先出的结构来遍历二叉树 remove和add方法 + * 2.借用一个stack后进先出的接口来遍历二叉树 pop和push方法 + *

+ * 用宽度优先搜索遍历来划分层次:[[1], [2, 3], [4, 5 , 6 ,7]]。 + * https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/er-cha-shu-de-ceng-ci-bian-li-by-leetcode/ + */ +public class LevelTraversal { + public static void main(String[] args) { + int[] arr = {1, 2, 3, 4, 5, 6, 7}; + List treeNodes = TreeUtils.buildTree(arr); + TreeNode root = treeNodes.get(0); + + //List> allNodes = levelOrder(root); + List> allNodes = levelOrder2(root); + + for (List nodesInALevel : allNodes) { + System.out.println(); + for (Integer node : nodesInALevel) { + System.out.print(node + " "); + } + } + } + + /** + * 返回二叉树的所有节点的值 + *

+ * 使用迭代的方式 + *

+ * 思路: 使用一个队列来装载二叉树的每一层节点, 通过先进先出的原理使得取出来的节点可以根据装入的节点顺序一样(根 左 右) + * + * @param root + * @return + */ + private static List> levelOrder(TreeNode root) { + // 创建一个2维的节点集合, 第1维度为层集合,第2维度为每层的节点集合 + List> levels = new ArrayList>(); + //如果根节点为空,直接返回空列表 + if (root == null) return levels; + + // 创建一个当前层的队列 + Queue queue = new LinkedList(); + queue.add(root); + + // 当前所在的层次 + int level = 0; + + while (!queue.isEmpty()) { + // 往节点结合中添加当前层级的空节点集合 + levels.add(new ArrayList()); + // 获取当前层次的节点个数 + int level_length = queue.size(); + // 遍历当前层的节点个数 + for (int i = 0; i < level_length; i++) { + TreeNode node = queue.remove();//取出并移除队列的第一个节点 根据先进先出原则, 取出的顺序为 根 左 右(根据添加时候的顺序一致) + + // 往节点集合中添加当前层级的节点 + levels.get(level).add(node.val); + + // 往当前层队列中添加当前层的子节点,用于下一次迭代处理 + if (node.left != null) + queue.add(node.left); + if (node.right != null) + queue.add(node.right); + } + // 层级+1,进入到下一个层级 + level++; + } + return levels; + } + + /** + * 使用 queue + * + * @param root + * @return + */ + private static List> levelOrder2(TreeNode root) { + List> nodes = new ArrayList>(); + + if (root == null) + return nodes; + + int level = 0; + Queue queue = new LinkedList(); + queue.add(root); + + // 遍历队列中的节点,当前层里的所有节点 + while (queue.size() > 0) { + nodes.add(new ArrayList()); + + // 当前层节点数 + int nodeCount = queue.size(); + + // 遍历当前层所有的节点, 往节点结合中添加当前层所有节点值, 往队列中添加当前所有节点的子节点 + for (int i = 0; i < nodeCount; i++) { + // 添加当前层节点值 + TreeNode node = queue.remove(); + nodes.get(level).add(node.val); + + if (node.left != null) + queue.add(node.left); + if (node.right != null) + queue.add(node.right); + } + + // 接着循环下一层 + level++; + } + return nodes; + } +} From 485a2af1980b7f30933a490e0caedc41e369456c Mon Sep 17 00:00:00 2001 From: Jason Wu Date: Sun, 5 Jan 2020 17:24:20 +0800 Subject: [PATCH 04/74] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=BD=92=E5=B9=B6?= =?UTF-8?q?=E7=9A=84=E6=96=B9=E5=BC=8F=E5=AF=B9=E6=9C=89=E5=BA=8F=E6=95=B0?= =?UTF-8?q?=E7=BB=84=E8=BF=9B=E8=A1=8C=E5=90=88=E5=B9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/array/MergeTwoASCSortedArray2.java | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 src/main/java/com/study/array/MergeTwoASCSortedArray2.java diff --git a/src/main/java/com/study/array/MergeTwoASCSortedArray2.java b/src/main/java/com/study/array/MergeTwoASCSortedArray2.java new file mode 100644 index 0000000..10b5c41 --- /dev/null +++ b/src/main/java/com/study/array/MergeTwoASCSortedArray2.java @@ -0,0 +1,154 @@ +package com.study.array; + +import java.util.Arrays; + +/** + * 在不开辟新数组的情况下 合并两个升序数组, 使用归并排序 + *

+ * 给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。 + *

+ * 说明: + *

+ * 初始化 nums1 和 nums2 的元素数量分别为 m 和 n。 + * 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。 + * 示例: + *

+ * 输入: + * nums1 = [1,2,3,0,0,0], m = 3 + * nums2 = [2,5,6], n = 3 + *

+ * 输出: [1,2,2,3,5,6] + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/merge-sorted-array + */ +public class MergeTwoASCSortedArray2 { + + public static void main(String[] args) { + int[] num1 = {2, 3, 4, 0, 0, 0, 0}; + int[] num2 = {1, 2, 5, 6}; + + MergeTwoASCSortedArray2 m = new MergeTwoASCSortedArray2(); + //m.merge(num1, 3, num2, 4); + //m.merge2(num1, 3, num2, 4); + //m.merge3(num1, 3, num2, 4); + m.merge4(num1,3,num2,4); + } + + /** + * 最简单的办法就是先合并,再排序. 时间复杂度较差为 O((n+m)log(n+m)) + * 因为没有用到两个数组本身已经排好顺序这一点 + * + * @param nums1 目标数组 + * @param m 目标数组开始放置新元素的位置 + * @param nums2 源数组 + * @param n 目标数组需要放置新元素的长度 + */ + public void merge(int[] nums1, int m, int[] nums2, int n) { + // 源数组 源数组开始的位置 目标数组 目标数组开始放置新元素的位置 目标数组需要放置新元素的长度 + System.arraycopy(nums2, 0, nums1, m, n); + Arrays.sort(nums1); + + print(nums1); + } + + /** + * 使用一个新数组 来存放合并结果 + * 时间复杂度 : O(n + m)O(n+m) + * 空间复杂度 : O(m)O(m) + * + * @param nums1 目标数组 + * @param m 目标数组开始放置新元素的位置 + * @param nums2 源数组 + * @param n 目标数组需要放置新元素的长度 + */ + public void merge2(int[] nums1, int m, int[] nums2, int n) { + // Make a copy of nums1. + int[] nums1_copy = new int[m]; + System.arraycopy(nums1, 0, nums1_copy, 0, m); + + // Two get pointers for nums1_copy and nums2. + int p1 = 0; + int p2 = 0; + + // Set pointer for nums1 + int p = 0; + + // Compare elements from nums1_copy and nums2 + // and add the smallest one into nums1. + while ((p1 < m) && (p2 < n)) + nums1[p++] = (nums1_copy[p1] < nums2[p2]) ? nums1_copy[p1++] : nums2[p2++]; + + // if there are still elements to add + if (p1 < m) + System.arraycopy(nums1_copy, p1, nums1, p1 + p2, m + n - p1 - p2); + if (p2 < n) + System.arraycopy(nums2, p2, nums1, p1 + p2, m + n - p1 - p2); + + print(nums1); + } + + /** + * 不开辟新的数组, 直接在nums1上面做调整, 从nums1的尾部开始插入元素 + * + * @param nums1 目标数组 + * @param m 目标数组开始放置新元素的位置 + * @param nums2 源数组 + * @param n 目标数组需要放置新元素的长度 + */ + public void merge3(int[] nums1, int m, int[] nums2, int n) { + int p1 = m - 1; // nums1数组有效元素的最大索引 + int p2 = n - 1; // nums2数组有效元素的最大索引 + + int p = m + n - 1; + // 将两个数组的有效元素从后往前做比较, 大的元素放在nums1的后面 + while (p1 >= 0 && p2 >= 0) { + if (nums1[p1] < nums2[p2]) { + nums1[p--] = nums2[p2--]; + } else { + nums1[p--] = nums1[p1--]; + } + } + + // 上面循环完, 可能因为mums2的元素更多,导致还有元素没有拷贝到nums1中 + // 将nums2剩余的最小的几个元素拷贝到mums1的最前面 + System.arraycopy(nums2, 0, nums1, 0, p2 + 1); + + print(nums1); + } + + + /** + * @param nums1 目标数组 + * @param m 目标数组从哪个位置开始放置源数组元素 + * @param nums2 源数组 + * @param n 需要将源数组元素拷贝到目标数组的个数 + */ + public void merge4(int[] nums1, int m, int[] nums2, int n) { + int p1 = m - 1; // p1为nums1有效元素的最大索引, 而m比nums1最大索引还要大1 + int p2 = n - 1;// p2为nums2的最大索引 + + int p = m + n - 1; // 新数组的最大索引 + + // 将nums1和nums2的有效元素从后往前做比较, 并将大的元素从后往前放到nums1中 + while (p1 >= 0 && p2 >= 0) { + if (nums1[p1] > nums2[p2]) { + nums1[p--] = nums1[p1--]; + } else { + nums1[p--] = nums2[p2--]; + } + } + + // 如果nums2的长度比nums1的有效长度还长, 那么循环结束后, nums2前面还有一些元素未拷贝到nums1中 + // 将nums2剩余的最小元素拷贝到nums1的前面 + System.arraycopy(nums2, 0, nums1, 0, p2 + 1); + + print(nums1); + } + + private static void print(int[] nums){ + for (int num : nums) { + System.out.println(num); + } + } +} From 9c4f9bca014354d4aab64a1f226d924cb86967fb Mon Sep 17 00:00:00 2001 From: Jason Wu Date: Sun, 5 Jan 2020 17:26:58 +0800 Subject: [PATCH 05/74] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 12 ++ .../study/binarytree/InorderTraversal.java | 7 +- .../study/binarytree/PostorderTraversal.java | 2 +- .../study/binarytree/PreorderTraversal.java | 4 +- .../study/linkedlist/DoubleLinkedNode.java | 68 ++++----- .../java/com/study/linkedlist/Reverse.java | 49 ++++++- .../study/linkedlist/SwapNodesInPairs.java | 34 ++++- .../java/com/study/number/ReverseInteger.java | 4 +- src/main/java/com/study/sort/QuickSort.java | 60 +++++++- src/main/java/com/study/sort/SelectSort.java | 7 +- .../stack/ImplementQueueUsingStacks.java | 130 +++++++++++++++--- src/main/java/com/study/trietree/Trie.java | 2 +- src/main/java/com/study/utils/TreeUtils.java | 2 - 13 files changed, 310 insertions(+), 71 deletions(-) diff --git a/pom.xml b/pom.xml index 919d74d..200cfc9 100644 --- a/pom.xml +++ b/pom.xml @@ -7,6 +7,18 @@ org.me.study datastructure-algorithm 1.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + + + \ No newline at end of file diff --git a/src/main/java/com/study/binarytree/InorderTraversal.java b/src/main/java/com/study/binarytree/InorderTraversal.java index 9a47ae4..a62c291 100644 --- a/src/main/java/com/study/binarytree/InorderTraversal.java +++ b/src/main/java/com/study/binarytree/InorderTraversal.java @@ -3,12 +3,11 @@ import com.study.utils.TreeUtils; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; import java.util.Stack; /** - * 94. 二叉树的中序遍历 + * 94. 二叉树的中序遍历 深度优先遍历dfs *

* 给定一个二叉树,返回它的中序 遍历。 *

@@ -56,10 +55,10 @@ private static List inorderTraversal(TreeNode root) { private static void traversal(TreeNode root, List list) { if (root != null) { - //递归左(右)子树 + //递归左子树 traversal(root.left, list); list.add(root.val); - //递归右(左)子树 + //递归右子树 traversal(root.right, list); } } diff --git a/src/main/java/com/study/binarytree/PostorderTraversal.java b/src/main/java/com/study/binarytree/PostorderTraversal.java index 1d23ef4..050c2d9 100644 --- a/src/main/java/com/study/binarytree/PostorderTraversal.java +++ b/src/main/java/com/study/binarytree/PostorderTraversal.java @@ -7,7 +7,7 @@ import java.util.Stack; /** - * 145. 二叉树的后序遍历 + * 145. 二叉树的后序遍历 深度优先遍历dfs *

* 给定一个二叉树,返回它的 后序 遍历。 *

diff --git a/src/main/java/com/study/binarytree/PreorderTraversal.java b/src/main/java/com/study/binarytree/PreorderTraversal.java index 2696242..8d424bb 100644 --- a/src/main/java/com/study/binarytree/PreorderTraversal.java +++ b/src/main/java/com/study/binarytree/PreorderTraversal.java @@ -7,9 +7,9 @@ import java.util.Stack; /** - * 144. 二叉树的前序遍历 + * 144. 二叉树的前序遍历 深度优先遍历dfs *

- * 给定一个二叉树,返回它的 前序 遍历。 + * 给定一个二叉树,返回它的 前序 遍历。 前序遍历: 根 - 左 - 右 *

* 示例: *

diff --git a/src/main/java/com/study/linkedlist/DoubleLinkedNode.java b/src/main/java/com/study/linkedlist/DoubleLinkedNode.java index ce5c31f..0ee9b27 100644 --- a/src/main/java/com/study/linkedlist/DoubleLinkedNode.java +++ b/src/main/java/com/study/linkedlist/DoubleLinkedNode.java @@ -17,9 +17,9 @@ public static void main(String[] args) { Node node1 = new Node(1); Node node2 = new Node(2); Node node3 = new Node(3); - Node node4 = new Node(5); - Node node5 = new Node(6); - Node node6 = new Node(7); + Node node4 = new Node(4); + Node node5 = new Node(5); + Node node6 = new Node(6); node1.next = node2; node2.prev = node1; @@ -32,36 +32,36 @@ public static void main(String[] args) { node5.next = node6; node6.prev = node5; - printLinkedList(node1); - - Node head1 = insertNode(node1, 0); - - printLinkedList(head1); - System.out.println(); - - Node head2 = insertNode(head1, 8); - - printLinkedList(head2); - System.out.println(); - Node head3 = insertNode(head2, 4); - - printLinkedList(head3); +// printLinkedList(node1); +// +// Node head1 = insertNode(node1, 0); +// +// printLinkedList(head1); +// System.out.println(); +// +// Node head2 = insertNode(head1, 8); +// +// printLinkedList(head2); +// System.out.println(); +// Node head3 = insertNode(head2, 4); +// +// printLinkedList(head3); System.out.println("---------------------"); - Node head4 = deleteNode(head3, 9); + Node head4 = deleteNode(node1, 3); printLinkedList(head4); - System.out.println(); - head4 = deleteNode(head4, 0); - printLinkedList(head4); - - System.out.println(); - head4 = deleteNode(head4, 5); - printLinkedList(head4); - - System.out.println(); - head4 = deleteNode(head4, 8); - printLinkedList(head4); +// System.out.println(); +// head4 = deleteNode(head4, 0); +// printLinkedList(head4); +// +// System.out.println(); +// head4 = deleteNode(head4, 5); +// printLinkedList(head4); +// +// System.out.println(); +// head4 = deleteNode(head4, 8); +// printLinkedList(head4); } @@ -80,13 +80,15 @@ private static Node insertNode(Node head, int value) { while (head != null) { // 如果插入的节点比链表里的每个节点都小 if (newNode.value < head.value) { + // 头节点 // 如果head是头节点, 将新节点插在头节点的前面 if (head.prev == null) { newNode.next = head; head.prev = newNode; return newNode; } else { - //如果当前head不是头节点,插入新节点的时候需要修改上一个节点的next,当前节点的pre和下一个节点的pre,当前节点的next 4个指针 + // 中间节点 + //如果当前head不是头节点,插入新节点的时候需要修改上一个节点的next,当前节点的pre和next, 以及下一个节点的pre, 4个指针 //修改当前节点的上一个节点, 将上一个节点存一个临时变量 Node prevNode = head.prev; //将上一个节点的临时变量的后继指针指向新节点, 将新节点的前驱指针指向上一个节点的临时变量 @@ -98,6 +100,7 @@ private static Node insertNode(Node head, int value) { return originalHead; } } + // 末尾节点 //如果新插入的节点比链表最后一个还要大 if (head.next == null) { //修改当前节点的后继和新节点的前驱 @@ -150,7 +153,7 @@ private static Node deleteNode(Node head, int value) { Node prev = head.prev; Node next = head.next; //将当前节点的前驱和后继改为null - head.prev = head.next = null; //这行代码也可以不写 + //head.prev = head.next = null; //这行代码也可以不写 //将上一个节点的next指向下一个节点 prev.next = next; //将下一个节点的prev指向上一个节点 @@ -199,7 +202,8 @@ private static void printLinkedList(Node head) { class Node { public int value; - public Node prev, next; + public Node prev; + public Node next; public Node(int value) { this.value = value; diff --git a/src/main/java/com/study/linkedlist/Reverse.java b/src/main/java/com/study/linkedlist/Reverse.java index 59c8ffb..86c3d56 100644 --- a/src/main/java/com/study/linkedlist/Reverse.java +++ b/src/main/java/com/study/linkedlist/Reverse.java @@ -32,9 +32,11 @@ public static void main(String[] args) { Printer.printLinkedList(node1); System.out.println(); - Printer.printLinkedList(reverseList(node1)); + //Printer.printLinkedList(reverseList(node1)); //Printer.printLinkedList(reverseList2(node1)); //Printer.printLinkedList(reverseList3(node1)); + //Printer.printLinkedList(reverseList4(node1)); + Printer.printLinkedList(reverstListByStack(node1)); } /** @@ -64,6 +66,25 @@ private static ListNode reverseList(ListNode head) { return prev; } + + private static ListNode reverseList4(ListNode head){ + ListNode prev = null; + + ListNode cur = head; + + while(cur != null){ + ListNode next = cur.next; + + cur.next = prev; + + prev = cur; + + cur = next; + } + + return prev; + } + /** * 使用递归的方法反转单链表 *

@@ -102,6 +123,7 @@ private static ListNode reverseList2(ListNode head) { /** * 借用stack后进先出的原理,对链表进行反转 + * 进的顺序为1 2 3 4 5, 出的顺序为 5 4 3 2 1 * * @param head * @return @@ -129,4 +151,29 @@ private static ListNode reverseList3(ListNode head) { } return prev.next; } + + + /** + * 利用栈的先进后出原理, 实现链表的反转 + * @param head + * @return + */ + private static ListNode reverstListByStack(ListNode head){ + // 先将链表每个节点压入栈中, 放入顺序为 1 2 3 4 5 + Stack stack = new Stack(); + while(head !=null){ + stack.push(new ListNode(head.val));// 将节点压栈的时候需要放入没有后继的节点 + head = head.next; + } + + // 把栈顶部的元素一个个弹出, 弹出顺序为5 4 3 2 1 + ListNode newNode = new ListNode(0); + ListNode newHead = newNode; + while (stack.size() > 0) { + newHead.next = stack.pop(); + newHead = newHead.next; + } + + return newNode.next; + } } diff --git a/src/main/java/com/study/linkedlist/SwapNodesInPairs.java b/src/main/java/com/study/linkedlist/SwapNodesInPairs.java index 99cf917..3933b56 100644 --- a/src/main/java/com/study/linkedlist/SwapNodesInPairs.java +++ b/src/main/java/com/study/linkedlist/SwapNodesInPairs.java @@ -36,13 +36,14 @@ public static void main(String[] args) { Printer.printLinkedList(node1); //Printer.printLinkedList(swapPairs(node1)); - Printer.printLinkedList(swapPairs2(node1)); + //Printer.printLinkedList(swapPairs2(node1)); + Printer.printLinkedList(swapPairs3(node1)); } /** * 通过循环遍历链表, 每次取未交换的前2个节点进行交换,交换完后将指针指向后2个节点,一遍下一轮循环处理 * - * 这里需要3个指针,其中两个是pair对中相邻的两个节点指针a和b,另一个是pair对之前的一个节点指针 + * 这里需要3个指针,其中两个是pair对中相邻的两个节点指针a和b,另一个是pair对之前的一个节点指针prev * * @param head * @return @@ -68,6 +69,35 @@ private static ListNode swapPairs(ListNode head) { return result.next; } + + /** + * 需要3个指针, 替换节点对的前继指针和 节点对的两个指针 + * @param head + * @return + */ + private static ListNode swapPairs3(ListNode head){ + ListNode headPrev = new ListNode(0); + headPrev.next = head; //在头节点前面在放一个前置节点, 用于后面的迭代使用 + ListNode prev = headPrev; + + // 每次循环都判断接下来的 一对节点不为空, 然后对他们进行对调 + while(prev.next !=null && prev.next.next !=null){ + ListNode oldFirst = prev.next; + ListNode oldSecond = prev.next.next; + + //将当前循环的第一和第二节点进行对换 + ListNode third = oldSecond.next; + prev.next = oldSecond; + oldSecond.next = oldFirst; + oldFirst.next = third; + + // 将下一次循环的两节点前继指针指向新的第2节点 + //prev = first; + prev = prev.next.next; + } + return headPrev.next; + } + /** * 使用递归 * diff --git a/src/main/java/com/study/number/ReverseInteger.java b/src/main/java/com/study/number/ReverseInteger.java index 7d9f73c..28bdad9 100644 --- a/src/main/java/com/study/number/ReverseInteger.java +++ b/src/main/java/com/study/number/ReverseInteger.java @@ -25,9 +25,9 @@ public class ReverseInteger { public static void main(String[] args) { - //int x = 1234567890; + int x = 1234567890; //int x = -1234567890; - int x = 0; + //int x = 0; System.out.println(reverse(x)); diff --git a/src/main/java/com/study/sort/QuickSort.java b/src/main/java/com/study/sort/QuickSort.java index 4ff818a..e1097f4 100644 --- a/src/main/java/com/study/sort/QuickSort.java +++ b/src/main/java/com/study/sort/QuickSort.java @@ -18,12 +18,13 @@ public class QuickSort { public static void main(String[] args) { - //int[] a = {12, 20, 5, 16, 15, 1, 30, 5, 23, 9}; - int[] a = {12, 20, 9, 5, 16}; + int[] a = {12, 20, 5, 16, 15, 1, 30, 45}; + // int[] a = {12, 20, 9, 5, 16}; //int[] a = {12, 1}; int start = 0; int end = a.length - 1; - sort(a, start, end); + //sort(a, start, end); + sort2(a, start, end); for (int i = 0; i < a.length; i++) { System.out.printf("%s ", a[i]); } @@ -51,13 +52,14 @@ private static void sort(int[] a, int low, int high) { // 选取第一个元素作为基准值 int key = a[low]; + // 循环里 start往后挪, end往前挪 while (end > start) { // 从后往前比较 - // 如果没有比基准值小的,比较下一个,直到有比基准值小的交换位置,交换的为start和end索引上的元素;然后又从前往后比较, + // 如果没有比基准值小的,比较下一个,直到有比基准值小的交换位置,将此时start和end上的元素进行交换;然后又从前往后比较, while (end > start && a[end] >= key){ end--; } - + // 把小于基准值的放到基准值左边 if (a[end] < key) { int temp = a[end]; a[end] = a[start]; @@ -68,6 +70,7 @@ private static void sort(int[] a, int low, int high) { while (end > start && a[start] <= key){ start++; } + // 把大于基准值的放到基准值右边 if (a[start] > key) { int temp = a[start]; a[start] = a[end]; @@ -86,4 +89,51 @@ private static void sort(int[] a, int low, int high) { sort(a, end + 1, high); } } + + private static void sort2(int[] arr, int low , int high){ + int start = low; + int end = high; + + // 取第0个元素为基准值 + int key = arr[start]; + + while(start < end){ + + // 后面的比基准值大的 + while(start < end && arr[end] >= key){ + end --; //往前挪 + } + + // 直到遇上比基准值小的, 前后替换 + if(arr[end] < key){ + int tmp = arr[end]; + arr[end] = arr[start]; + arr[start] = tmp; + } + + // 前面比基准值小的 + while(start < end && arr[start] <= key){ + start ++; //往后挪 + } + + if(arr[start] > key){ + int tmp = arr[start]; + arr[start] = arr[end]; + arr[end] = tmp; + } + } + + // 循环结束后, 基准值前面的都比它小, 后面的都比它大. 但顺序还不是有序的 + + // 左边, 第一个索引位置到基准值-1 + if(start > low){ + System.out.println("low" + low); + sort2(arr, low, start - 1); + } + // 右边, 基准索引+1到最后一个 + if(end < high){ + System.out.println("high" + high); + sort2(arr, end+1 , high); + } + } } diff --git a/src/main/java/com/study/sort/SelectSort.java b/src/main/java/com/study/sort/SelectSort.java index 8b791b7..071b5e2 100644 --- a/src/main/java/com/study/sort/SelectSort.java +++ b/src/main/java/com/study/sort/SelectSort.java @@ -30,6 +30,9 @@ public static void main(String[] args) { /** * 选择排序, 从每次遍历未排序的元素中找出值最小的元素索引 * + * 每一次循环能找出一个当前最小的索引,然后将最小索引上的元素放在当前遍历的索引上 + * + * 跟冒泡排序类似,只是它不会做元素的两两替换,而是找最大或最小索引,找到后再替换 */ private static void selectSort(int[] a) { //由于最后一个元素无需再遍历了,只要前面的所有元素都是按顺序从小到大排列,那么最后一个就是最大值, 所以只要判断i < a.length-1即可 @@ -42,7 +45,7 @@ private static void selectSort(int[] a) { min = j; } } - //将最小的索引为m的元素放到前面k位置上 + //将前面未排序的元素与后面最小元素做替换 if (i != min) { int temp = a[i]; a[i] = a[min]; @@ -60,7 +63,7 @@ private static void selectSortDesc(int[] a) { int max = i; //将后面的元素与前面元素做比较,如果后面的大,则把max索引改为最大的元素索引 for (int j = i + 1; j < a.length; j++) { - if (a[max] < a[j]) { + if (a[j] > a[max]) { max = j; } } diff --git a/src/main/java/com/study/stack/ImplementQueueUsingStacks.java b/src/main/java/com/study/stack/ImplementQueueUsingStacks.java index ac1618b..903e1ab 100644 --- a/src/main/java/com/study/stack/ImplementQueueUsingStacks.java +++ b/src/main/java/com/study/stack/ImplementQueueUsingStacks.java @@ -1,42 +1,48 @@ package com.study.stack; -import java.util.Stack; +import java.util.*; +import java.util.function.Consumer; /** * 232. 用栈实现队列 - * + *

* 使用栈实现队列的下列操作: - * + *

* push(x) -- 将一个元素放入队列的尾部。 * pop() -- 从队列首部移除元素。 * peek() -- 返回队列首部的元素。 * empty() -- 返回队列是否为空。 * 示例: - * + *

* MyQueue queue = new MyQueue(); - * + *

* queue.push(1); * queue.push(2); * queue.peek(); // 返回 1 * queue.pop(); // 返回 1 * queue.empty(); // 返回 false * 说明: - * + *

* 你只能使用标准的栈操作 -- 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。 * 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。 * 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。 - * + *

* https://leetcode-cn.com/problems/implement-queue-using-stacks */ public class ImplementQueueUsingStacks { public static void main(String[] args) { - MyQueue obj = new MyQueue(); + //MyQueue obj = new MyQueue(); + //MyQueueByStack2 obj = new MyQueueByStack2(); + MyQueueByList obj = new MyQueueByList(); obj.push(1); obj.push(2); + obj.push(3); //根据队列先入先出的原理, 下面pop和peak都应该是第一个元素1 System.out.println(obj.pop()); - System.out.println(obj.peek()); - System.out.println(obj.empty()); + System.out.println(obj.pop()); + System.out.println(obj.pop()); +// System.out.println(obj.peek()); +// System.out.println(obj.empty()); } } @@ -45,10 +51,10 @@ public static void main(String[] args) { * 使用两个栈来实现一个队列 * 比如将1-2-3放入第一个栈里变成3-2-1 先进的到了最底下 * 再将第一个栈的元素3-2-1放到第2个栈里变成了1-2-3 先进的到了最底下 - * + *

* 最后使用栈的pop方法将最顶上的元素1移除,之后是2,3 */ -class MyQueue { +class MyQueueByStack { private Stack input; private Stack output; @@ -56,23 +62,25 @@ class MyQueue { /** * Initialize your data structure here. */ - public MyQueue() { + public MyQueueByStack() { input = new Stack(); output = new Stack(); } /** * Push element x to the back of queue. + *

+ * 需要借助两个stack进行元素的颠倒, 1 2 3 入input栈后 变成 3 2 1, 再取出入output栈后变成 1 2 3, 就成了先进先出的效果 */ public void push(int x) { //在往input栈中方元素之前, 需要把output栈里的元素全部放回input栈 - while(!output.isEmpty()){ + while (!output.isEmpty()) { input.push(output.pop()); } //然后再将元素x放入input input.push(x); //最后再将所有元素放入到output栈, 这样从output出去第一个的元素为x - while(!input.isEmpty()){ + while (!input.isEmpty()) { output.push(input.pop()); } } @@ -81,7 +89,7 @@ public void push(int x) { * Removes the element from in front of queue and returns that element. */ public int pop() { - if(output.isEmpty()){ + if (output.isEmpty()) { return -1; } return output.pop(); @@ -91,7 +99,7 @@ public int pop() { * Get the front element. */ public int peek() { - if(output.isEmpty()){ + if (output.isEmpty()) { return -1; } return output.peek(); @@ -104,3 +112,91 @@ public boolean empty() { return output.isEmpty(); } } + +/** + * 使用两个栈LIFO实现FIFO队列 + */ +class MyQueueByStack2 { + + private Stack inputStack = new Stack(); + private Stack outputStack = new Stack(); + + public void push(int x) { + // 先将元素放到input栈中, 然后再转入output栈中 + + // 将outputStack中的元素1 2 3全部取出放到inputStack中变成 3 2 1 + while (!outputStack.isEmpty()) { + inputStack.push(outputStack.pop()); + } + + // 将新入的元素放到inputStack顶部 顺序变成了 x 3 2 1 + inputStack.push(x); + + // 再将inputStack中的元素x 3 2 1取出放到outputStack中变成 1 2 3 x + while (!inputStack.isEmpty()) { + outputStack.push(inputStack.pop()); + } + } + + /** + * 取出并移除栈顶元素 + * + * @return + */ + public int pop() { + if (outputStack.isEmpty()) + return -1; + return outputStack.pop(); + } + + /** + * 取出不移除栈顶元素 + * + * @return + */ + public int peek() { + if (outputStack.isEmpty()) + return -1; + return outputStack.peek(); + } + + /** + * 获取堆中元素总个数 + * + * @return + */ + public int size() { + return outputStack.size(); + } +} + +class MyQueueByList { + + List result = new ArrayList(); + + public void push(int x) { + result.add(x); + } + + /** + * 将第一个元素弹出, 并移除该元素 + * @return + */ + public int pop() { + if (result.isEmpty()) + return -1; + int element = result.get(0); + result.remove(0); + return element; + } + + /** + * 将第一个元素弹出, 不移除该元素 + * @return + */ + public int peek() { + if (result.isEmpty()) + return -1; + return result.get(0); + } +} diff --git a/src/main/java/com/study/trietree/Trie.java b/src/main/java/com/study/trietree/Trie.java index ab90b3d..09be120 100644 --- a/src/main/java/com/study/trietree/Trie.java +++ b/src/main/java/com/study/trietree/Trie.java @@ -45,7 +45,7 @@ public static void insert(String str, TrieNode root) { return; } TrieNode node = root; - char[] letters = str.toCharArray();//将目标单词转换为字符数组 + char[] letters = str.toCharArray();//将目标单词转换为char数组 for (int i = 0, len = str.length(); i < len; i++) { //计算每个char的位置 int pos = letters[i] - 'a'; diff --git a/src/main/java/com/study/utils/TreeUtils.java b/src/main/java/com/study/utils/TreeUtils.java index ead7edb..bf35330 100644 --- a/src/main/java/com/study/utils/TreeUtils.java +++ b/src/main/java/com/study/utils/TreeUtils.java @@ -20,7 +20,6 @@ public static List buildTree(int[] nodes){ treeNodes.add(new TreeNode(data)); } //创建一个二叉树 - TreeNode root = treeNodes.get(0); //创建其他节点 每个节点的左子节点都是这个节点的2倍+1, 右子节点都是这个节点的2倍+2 for (int i = 0; i < nodes.length / 2; i++) { treeNodes.get(i).left = treeNodes.get(i * 2 + 1); @@ -28,7 +27,6 @@ public static List buildTree(int[] nodes){ treeNodes.get(i).right = treeNodes.get(i * 2 + 2); } } - return treeNodes; } From e0f77ac998ad48d4af9fe375d71e271b88b56d90 Mon Sep 17 00:00:00 2001 From: Jason Wu Date: Sun, 5 Jan 2020 23:20:44 +0800 Subject: [PATCH 06/74] =?UTF-8?q?=E8=AE=A1=E6=95=B0=E8=B4=A8=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/number/CountPrime.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/main/java/com/study/number/CountPrime.java diff --git a/src/main/java/com/study/number/CountPrime.java b/src/main/java/com/study/number/CountPrime.java new file mode 100644 index 0000000..31b5471 --- /dev/null +++ b/src/main/java/com/study/number/CountPrime.java @@ -0,0 +1,62 @@ +package com.study.number; + +import java.util.ArrayList; +import java.util.List; + +/** + * 204. 计数质数 + *

+ * 统计所有小于非负整数 n 的质数的数量。 + *

+ * 示例: + *

+ * 输入: 10 + * 输出: 4 + * 解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。 + *

+ * https://leetcode-cn.com/problems/count-primes/ + */ +public class CountPrime { + public static void main(String[] args) { + CountPrime c = new CountPrime(); + List primeList = c.countPrimes(10); + for (Integer p : primeList) { + System.out.println(p); + } + } + + /** + * 计算n以内所有质数的个数 + * + * @param n + * @return + */ + public List countPrimes(int n) { + List primeList = new ArrayList<>(); + for (int i = 0; i <= n; i++) { + if (isPrime(i)) + primeList.add(i); + } + return primeList; + } + + public boolean isPrime(int num) { + // 0和1不是质数 + if (num <= 1) + return false; + + // 求开方根 会导致结果不准 +// int numSq = (int) Math.sqrt(num); +// for (int i = 2; i <= numSq; i++) { +// if (numSq % i == 0) { +// return false; +// } +// } + for (int i = 2; i < num; i++) { + if (num % i == 0) { + return false; + } + } + return true; + } +} From 4cfc2dd832448d0f90cb5ba55f89cf60507ce2d1 Mon Sep 17 00:00:00 2001 From: Jason Wu Date: Mon, 6 Jan 2020 00:43:25 +0800 Subject: [PATCH 07/74] =?UTF-8?q?=E8=AE=A1=E7=AE=97=E6=9C=80=E9=95=BF?= =?UTF-8?q?=E5=9B=9E=E6=96=87=E4=B8=B2=E7=9A=84=E5=AD=97=E7=AC=A6=E4=B8=AA?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/study/string/LongestPalindrome.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/main/java/com/study/string/LongestPalindrome.java diff --git a/src/main/java/com/study/string/LongestPalindrome.java b/src/main/java/com/study/string/LongestPalindrome.java new file mode 100644 index 0000000..f0b337b --- /dev/null +++ b/src/main/java/com/study/string/LongestPalindrome.java @@ -0,0 +1,75 @@ +package com.study.string; + + +import java.util.HashMap; +import java.util.Map; + +/** + * 计算最长回文串的字符个数 + * + * 回文串是一个正读和反读都一样的字符串。对一个左边的字符 i 右边一定会有一个对称 i。比如 'abcba', 'aa','bb' 这几个回文串。其中第一个有点特殊,中间的 c 是唯一的。 + * 如果让你来造一个回文串你会怎么造? 首先让它左右两边绝对对称,如果可能的话再加上一个唯一的中心。 + *

+ *

+ *

+ * 链接:https://leetcode-cn.com/problems/longest-palindrome/solution/zui-chang-hui-wen-chuan-by-leetcode/ + */ +public class LongestPalindrome { + + public static void main(String[] args) { + //String s = "abcbad"; + //String s = "a"; + String s = "abccccdd"; + System.out.println(1 / 2 * 2); + System.out.println(3 / 2 * 2); + System.out.println(5 / 2 * 2); + System.out.println(0 / 2 * 2); + System.out.println("--------------------"); + + LongestPalindrome lp = new LongestPalindrome(); + //System.out.println(lp.longestPalindrome(s)); + System.out.println(lp.count(s)); + } + + public int longestPalindrome(String s) { + // ASCII总共128个字符 + int[] count = new int[128]; + for (char c : s.toCharArray()) { + count[c]++; //记录每个char的数量 + } + + int ans = 0; + for (int v : count) { + // 统计元素出现的总个数, 必须是出现个数>=2以上的才满足, 而且取其最大偶数次数, 比如2次的ans=2, 3次的ans=2,5次的ans=4 + ans += v / 2 * 2; + // 用于找出中心字符, 条件是中心字符出现次数为奇数次比如1 3 5等,且当前其他元素出现的总次数为偶数(如 0 2 4等)就满足条件 + if (v % 2 == 1 && ans % 2 == 0) + ans++; + } + return ans; + } + + + public int count(String s) { + // 记录每个char出现的次数 + Map kv = new HashMap<>(); + for (char c : s.toCharArray()) { + if (!kv.containsKey(c)) + kv.put(c, 1); + else + kv.put(c, kv.get(c) + 1); + } + + // 计算回文数总个数 + int count = 0; + for (Map.Entry entry : kv.entrySet()) { + // 统计元素出现的总个数, 必须是出现个数>=2以上的才满足, 而且取其最大偶数次数, 比如2次的count=2, 3次的count=2,5次的count=4 + count += entry.getValue() / 2 * 2; + // 只有之前出现的总个数为偶数, 才允许将出现一次的元素加到总数 + if (count % 2 == 0 && entry.getValue() % 2 == 1) { + count++; + } + } + return count; + } +} From 1973a8cf621e00ee8da06fd6d86056de4d3b864d Mon Sep 17 00:00:00 2001 From: Jason Wu Date: Mon, 6 Jan 2020 20:26:53 +0800 Subject: [PATCH 08/74] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E7=AE=80=E5=8D=95=E7=9A=84hashmap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/hash/MyHashMapClient.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/main/java/com/study/hash/MyHashMapClient.java diff --git a/src/main/java/com/study/hash/MyHashMapClient.java b/src/main/java/com/study/hash/MyHashMapClient.java new file mode 100644 index 0000000..2b22a68 --- /dev/null +++ b/src/main/java/com/study/hash/MyHashMapClient.java @@ -0,0 +1,75 @@ +package com.study.hash; + +/** + * 一个简单的hashmap, 主要用于key value存储, 查找,插入,删除的时间复杂度为O1 + */ +public class MyHashMapClient { + public static void main(String[] args) { + MyHashMap hashMap = new MyHashMap(10); + hashMap.put("aaa","jack"); + hashMap.put("bbb","mike"); + + System.out.println(hashMap.get("aaa"));// O1的时间复杂度 + } +} + +/** + * 一个简易的hashmap + * + * @param + * @param + */ +class MyHashMap { + + private int size; + private Node[] table; + + public MyHashMap(int size) { + this.size = size; + table = (Node[]) new Node[size]; + } + + public void put(K key, V value) { + int hash = hash(key); + table[hash] = new Node<>(hash, key, value, null); + } + + public V get(K key) { + return table[hash(key)].getValue(); + } + + private int hash(K key) { + int h = key.hashCode(); +// int v = h >>>16; +// System.out.println(h ^ v); + return h % this.size; + } + + static class Node { + final int hash; + final K key; + V value; + Node next; + + Node(int hash, K key, V value, Node next) { + this.hash = hash; + this.key = key; + this.value = value; + this.next = next; + } + + public final K getKey() { + return key; + } + + public final V getValue() { + return value; + } + + public final V setValue(V newValue) { + V oldValue = value; + value = newValue; + return oldValue; + } + } +} From 0520de4c0e1e9e0599c55ac8975c9ff08e4434e9 Mon Sep 17 00:00:00 2001 From: Jason Wu Date: Mon, 6 Jan 2020 22:05:58 +0800 Subject: [PATCH 09/74] =?UTF-8?q?=E6=9C=89=E6=95=88=E7=9A=84=E5=AD=97?= =?UTF-8?q?=E6=AF=8D=E5=BC=82=E4=BD=8D=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/string/ValidAnagram.java | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 src/main/java/com/study/string/ValidAnagram.java diff --git a/src/main/java/com/study/string/ValidAnagram.java b/src/main/java/com/study/string/ValidAnagram.java new file mode 100644 index 0000000..348b2f2 --- /dev/null +++ b/src/main/java/com/study/string/ValidAnagram.java @@ -0,0 +1,133 @@ +package com.study.string; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * 242. 有效的字母异位词 + *

+ * 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 + * 示例 1: + *

+ * 输入: s = "anagram", t = "nagaram" + * 输出: true + * 示例 2: + *

+ * 输入: s = "rat", t = "car" + * 输出: false + * 说明: + * 你可以假设字符串只包含小写字母。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/valid-anagram + */ +public class ValidAnagram { + + /** + * 两种解法, + * 一种是 对字符串中所有字符转成字符数组进行排序, 然后遍历数组对比每个字符是否, 如果都一样则是 + * 一种是 使用hashmap来存储两个字符串里的每个字符以及个数, 判断两个hashmap是否相同. + * + * @param args + */ + public static void main(String[] args) { + String s1 = "cat"; + String s2 = "tac"; + ValidAnagram va = new ValidAnagram(); + //System.out.printf("%s==%s: %s", s1, s2, va.isAnagram(s1, s2)); + System.out.printf("%s==%s: %s", s1, s2, va.isAnagram2(s1, s2)); + System.out.println(); + + s1 = "aaab"; + s2 = "bbaa"; + //System.out.printf("%s==%s: %s", s1, s2, va.isAnagram(s1, s2)); + System.out.printf("%s==%s: %s", s1, s2, va.isAnagram2(s1, s2)); + System.out.println(); + + s1 = "anagram"; + s2 = "nagaram"; + System.out.printf("%s==%s: %s", s1, s2, va.isAnagram2(s1, s2)); + System.out.println(); + } + + + /** + * 使用排序的方式进行比较 时间复杂度为O(nlogn) + *

+ * 对字符串中所有字符进行排序, 然后将两个字符串的每个字符进行比较, 如果每个字符都一样,且字符串长度相同 那么是字母异位词 + * + * @param s + * @param t + * @return + */ + public boolean isAnagram(String s, String t) { + // 判断字符串长度是否一样, 不一样直接返回false + if (s.length() != t.length()) + return false; + + // 将两个字符串转成char数组 + char[] chars1 = s.toCharArray(); + char[] chars2 = t.toCharArray(); + + // 使用Arrays.sort对char数组进行排序 (内部是快速排序,时间复杂度为O(nlogn)) + Arrays.sort(chars1); + Arrays.sort(chars2); + + // 将排序好的数组进行比较, 比较每一位的字符是否一样 + for (int i = 0; i < chars1.length; i++) { + if (chars1[i] != chars2[i]) + return false; + } + return true; + } + + /** + * 借助hashmap进行比较 时间复杂度为O(n) + *

+ * 使用hashmap来存储两个字符串里的每个字符以及个数, 判断两个hashmap里面的key和value是否相同. + * + * @param s + * @param t + * @return + */ + public boolean isAnagram2(String s, String t) { + // 长度不一样,直接返回false + if (s.length() != t.length()) + return false; + + HashMap mapS = new HashMap<>(); + HashMap mapT = new HashMap<>(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (mapS.containsKey(c)) { + mapS.put(c, mapS.get(c) + 1); + } else { + mapS.put(c, 1); + } + } + + for (int i = 0; i < t.length(); i++) { + char c = t.charAt(i); + if (mapT.containsKey(c)) { + mapT.put(c, mapT.get(c) + 1); + } else { + mapT.put(c, 1); + } + } + + // 因为前面判断了是否长度一样 + // 所以这里只需要判断每个key对应的value是否一样 + for (Map.Entry kv : mapS.entrySet()) { + if (!kv.getValue().equals(mapT.get(kv.getKey()))) + return false; + } + + return true; + } + +// +// public boolean isAnagram2(String s, String t) { +// +// } +} From a7e815e68b497676eb8c706b18e4c0ba7edbccf4 Mon Sep 17 00:00:00 2001 From: Jason Wu Date: Mon, 6 Jan 2020 22:52:27 +0800 Subject: [PATCH 10/74] =?UTF-8?q?=E8=AE=A1=E7=AE=97N=E7=9A=84=E9=98=B6?= =?UTF-8?q?=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/number/FactorialN.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/main/java/com/study/number/FactorialN.java diff --git a/src/main/java/com/study/number/FactorialN.java b/src/main/java/com/study/number/FactorialN.java new file mode 100644 index 0000000..6b7ba76 --- /dev/null +++ b/src/main/java/com/study/number/FactorialN.java @@ -0,0 +1,47 @@ +package com.study.number; + +/** + * 计算N的阶层 + * + * https://time.geekbang.org/course/detail/130-42710 + */ +public class FactorialN { + + public static void main(String[] args) { + int n = 5; + int result = factorialN(n); // 5 * 4 * 3 * 2 * 1 + System.out.println(String.format("%d的阶层为: %d", n, result)); + result = factorialNByLoop(n); + System.out.println(String.format("%d的阶层为: %d", n, result)); + } + + /** + * 计算n的阶层 n! = 1 * 2 * 3... * n 或者 n * (n-1) * (n-2) ... * 2 * 1 + * 使用递归的方式 + * + * @param n + * @return + */ + private static int factorialN(int n) { + // 递归结束条件是n==1 + if (n == 1) + return 1; + // 由于factorialN调用自己, 所以每次进来n都比上一次少1 + return n * factorialN(n - 1); // 通过 n * (n -1) * (n - 2) * .... * 1 + } + + /** + * 使用迭代的方式来实现递归的效果 + * + * @param n + * @return + */ + private static int factorialNByLoop(int n) { + int result = 1; + while (n >= 1) {// 当n减少到1的时候退出循环 + result = result * n; + n--;// 每次减一 + } + return result; + } +} From f93c6ef0481d36e7fa87532a04c0d679d1bc89f3 Mon Sep 17 00:00:00 2001 From: Jason Wu Date: Mon, 6 Jan 2020 23:46:30 +0800 Subject: [PATCH 11/74] =?UTF-8?q?n=E7=9A=84=E7=AD=89=E5=B7=AE=E6=95=B0?= =?UTF-8?q?=E5=88=97=E6=B1=82=E5=92=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../number/SumofArithmeticSequences.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/main/java/com/study/number/SumofArithmeticSequences.java diff --git a/src/main/java/com/study/number/SumofArithmeticSequences.java b/src/main/java/com/study/number/SumofArithmeticSequences.java new file mode 100644 index 0000000..de47673 --- /dev/null +++ b/src/main/java/com/study/number/SumofArithmeticSequences.java @@ -0,0 +1,42 @@ +package com.study.number; + +/** + * n的等差数列求和 + */ +public class SumofArithmeticSequences { + + public static void main(String[] args) { + int n = 10; + System.out.println(sum(n)); + System.out.println(sum2(n)); + } + + /** + * 使用递归实现 n的等差数列求和 + * + * @param n + * @return + */ + private static int sum(int n) { + // 当n=1的时候 结束迭代 + if (n == 1) + return 1; + return n + sum(n - 1); // n + (n-1) + (n-2) + ... + 1; + } + + /** + * 使用迭代实现 n的等差数列求和 + * + * @param n + * @return + */ + private static int sum2(int n) { + int result = 0; + while (n >= 1) { + result += n; + n--; + } + return result; + } + +} From 69f8d9339a30d6f452caa4718fad37b40f048a57 Mon Sep 17 00:00:00 2001 From: Jason Wu Date: Tue, 7 Jan 2020 23:54:10 +0800 Subject: [PATCH 12/74] =?UTF-8?q?=E5=90=88=E5=B9=B6=E4=B8=A4=E4=B8=AA?= =?UTF-8?q?=E6=9C=89=E5=BA=8F=E9=93=BE=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/study/linkedlist/MergeTwoLists.java | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 src/main/java/com/study/linkedlist/MergeTwoLists.java diff --git a/src/main/java/com/study/linkedlist/MergeTwoLists.java b/src/main/java/com/study/linkedlist/MergeTwoLists.java new file mode 100644 index 0000000..58ed6ef --- /dev/null +++ b/src/main/java/com/study/linkedlist/MergeTwoLists.java @@ -0,0 +1,159 @@ +package com.study.linkedlist; + +import com.study.utils.Printer; + +/** + * 合并两个有序链表 + * 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。  + *

+ * 示例: + *

+ * 输入:1->2->4, 1->3->4 + * 输出:1->1->2->3->4->4 + *

+ * https://leetcode-cn.com/problems/merge-two-sorted-lists/ + *

+ * https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/hua-jie-suan-fa-21-he-bing-liang-ge-you-xu-lian-bi/ + */ +public class MergeTwoLists { + + public static void main(String[] args) { + ListNode l1 = new ListNode(1); + l1.next = new ListNode(2); + l1.next.next = new ListNode(4); + + ListNode l2 = new ListNode(1); + l2.next = new ListNode(3); + l2.next.next = new ListNode(4); + l2.next.next.next = new ListNode(6); + + System.out.println("l1链表"); + Printer.printLinkedList(l1); + System.out.println("l2链表"); + Printer.printLinkedList(l2); + + // ListNode result = mergeTwoLists(l1, l2); + // ListNode result = mergeTwoListsByRecursion(l1,l2); + ListNode result = mergeTwoListsByRecursion2(l1, l2); + Printer.printLinkedList(result); + } + + /** + * 创建一个新链表 + * 迭代的方式将两个链表中的小值加入到一个新链表中, 迭代完后需要把没有处理完的列表加到新链表尾部. 时间复杂度O(m+n) + *

+ * 时间复杂度 O(m+n) m和n各位两个链表的长度 + * + * @param l1 + * @param l2 + * @return + */ + public static ListNode mergeTwoLists(ListNode l1, ListNode l2) { + ListNode prev = new ListNode(-1); + ListNode head = prev; + + // 先将l1和l2中小的元素依次放入新链表中 + while (l1 != null && l2 != null) { + // 将小的加入到链表前面 + if (l1.val > l2.val) { + head.next = new ListNode(l2.val); + l2 = l2.next; + } else { + head.next = new ListNode(l1.val); + l1 = l1.next; + } + Printer.printLinkedList(prev.next); + head = head.next; + } + + // 由于两个链表长度和节点值原因, 可能存在某个链表没有遍历完成 + + // 如果l1没有遍历完成 +// while (l1 != null) { +// head.next = new ListNode(l1.val); +// head = head.next; +// l1 = l1.next; +// } +// +// // 如果l2没有遍历完成 +// while (l2 != null) { +// head.next = new ListNode(l2.val); +// head = head.next; +// l2 = l2.next; +// } + + // 针对上面注释的代码进行优化 + // 因为最终只可能有一条链表没有遍历完成, 而且链表本身也是有序的,所以直接加到新链表的next即可 + head.next = l1 == null ? l2 : l1; + Printer.printLinkedList(prev.next); + return prev.next; + } + + /** + * 使用递归的方式, 将链表从大到小(node.next)进行组装, 时间复杂度为O(m+n) + * + * 递归往内直到终止条件前,都是按值小的先递归,这样一旦递归条件终止, 之后开始递归计算, 就把返回的剩下的大节点附加到小节点的.next后面, + * + * 依次往外计算, 小的.next = 大的, 最后得到结果 从小到大的完整链表 + * + * @param l1 + * @param l2 + * @return + */ + public static ListNode mergeTwoListsByRecursion(ListNode l1, ListNode l2) { + // 如果l1递归结束,说明l2剩下的节点比l1大, 于是返回l2, 让前面的l1.next接上 + if (l1 == null) { + System.out.print("l1递归往内终止完成, l2 = "); + Printer.printLinkedList(l2); + return l2; + } + // 如果l2递归结束, 说明l1剩下的节点比l2大, 于是返回l1, 让前面的l2.next接上 + else if (l2 == null) { + System.out.print("l2递归往内终止完成, l1 = "); + Printer.printLinkedList(l1); + return l1; + // 递归的顺序是从小的节点开始往内递归, 从小往大,所以到了最内层结束都的节点都是最大的. 之后从内层外外计算,是从大往小计算. + } else if (l1.val < l2.val) { + // 如果l1的当前节点比l2小, 那么l1.next与排序好的表头相接, 为什么是接表头? 因为递归是从里往外开始计算的, + // 里是最后一个节点(最大的节点), 而l1是前面的小节点, 顺序是从小到大 + System.out.println("l1递归往内找终止条件"); + Printer.printLinkedList(l1); + // 对l1进行递归遍历 + // 真正计算是从递归最里层往外 + l1.next = mergeTwoListsByRecursion(l1.next, l2);// l1.next 后面接的是递归里层的比自己大的节点 + System.out.println("l1递归往外,开始计算:"); + Printer.printLinkedList(l1); + return l1;// 返回l1, 供外层递归的 l1或者l2中的 + } else { + // 如果l2的节点小于l1, 那么使用l2的next与排序好的表头相接 + System.out.println("l2递归往内找终止条件"); + Printer.printLinkedList(l2); + // 对l2开始递归, 真正计算是从递归最里层往外, 从小往大, l2.next 接的节点都是比它大的 + l2.next = mergeTwoListsByRecursion(l1, l2.next); + System.out.println("l2递归往外,开始计算:"); + Printer.printLinkedList(l2); + return l2; + } + } + + + public static ListNode mergeTwoListsByRecursion2(ListNode l1, ListNode l2) { + // 哪个链表先递归到终止条件为null, 说明另一个链表的剩下节点比该链表大, 于是返回对方剩余链表, 加在自己的.next后面 + if (l1 == null) { + return l2; + } else if (l2 == null) { + return l1; + } + + // 先递归小的, 然后将后面大的放在小的.next上面 + if (l1.val < l2.val) { + // l1的当前节点小就先递归l1 + l1.next = mergeTwoListsByRecursion2(l1.next, l2); + return l1; + } else { + // l2的当前节点小那就递归l2 + l2.next = mergeTwoListsByRecursion2(l1, l2.next); + return l2; + } + } +} From 53649d6ce2597719676342739f9c08bcb32152d4 Mon Sep 17 00:00:00 2001 From: Jason Wu Date: Wed, 8 Jan 2020 22:10:18 +0800 Subject: [PATCH 13/74] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=C2=A0pow(x,=20n)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/study/number/PowXN.java | 198 ++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 src/main/java/com/study/number/PowXN.java diff --git a/src/main/java/com/study/number/PowXN.java b/src/main/java/com/study/number/PowXN.java new file mode 100644 index 0000000..cbd67d5 --- /dev/null +++ b/src/main/java/com/study/number/PowXN.java @@ -0,0 +1,198 @@ +package com.study.number; + +/** + * 实现 pow(x, n) ,即计算 x 的 n 次幂函数。 + *

+ * 示例 1: + *

+ * 输入: 2.00000, 10 + * 输出: 1024.00000 + * 示例 2: + *

+ * 输入: 2.10000, 3 + * 输出: 9.26100 + * 示例 3: + *

+ * 输入: 2.00000, -2 + * 输出: 0.25000 + * 解释: 2-2 = 1/22 = 1/4 = 0.25 + * 说明: + *

+ * -100.0 < x < 100.0 + * n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1] 。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/powx-n + */ +public class PowXN { + + public static void main(String[] args) { + double result = myPow(2.1000, 10); + System.out.println(result); +// +// double result = myPow2(2.1000, 3); +// System.out.println(result); + + //double result = myPow3(2.1000, -3); +// double result = myPow3(2.1000, 10); +// System.out.println(result); + +// result = myPowRecursion(2.1000, 10); +// System.out.println(result); + +// result = myPowRecursion(2, 10); +// System.out.println(result); + + result = myPowRecursion2(2, 10); + System.out.println(result); + } + + /** + * 暴力法, 循环将x乘以自己n次, 时间复杂度O(n) + * 空间复杂度O(1) 只需要一个变量来保存最终x的连乘结果 + * + * @param x + * @param n + * @return + */ + public static double myPow(double x, int n) { + double result = 1; + + // 正数次幂 + if (n > 0) { + while (n > 0) { + result *= x; + n--; + } + // 负数次幂 + } else { + n = -n; + while (n > 0) { + result *= x; + n--; + } + result = 1 / result; + } + return result; + } + + /** + * 暴力法, 优化n为负数的情况. + * + * @param x + * @param n + * @return + */ + public static double myPow2(double x, int n) { + long N = n; + if (N < 0) { + // 当n为负数的情况下, 实际上就是 1/x 的n次方 + x = 1 / x; + N = -N; + } + double ans = 1; + for (long i = 0; i < N; i++) + ans = ans * x;// 将x乘以自身n次 + return ans; + } + + /** + * 暴力法(优化), 乘以自己的次数改为n/2次, x的2n次方 = x的n次方 * x的n次方. + * 如 temp = x的n/2次方 然后结果为temp * temp即可. 比如 5的10次方 改为 a = 5的5次方, a * a = 5的10次方 + * 这里需要考虑n为偶数还是奇数, 如果为偶数, 那结果为temp * temp; 如果为奇数需要 n/2实际上为(n-1)/2, 所以结果为 temp * temp * x; + * + * @param x + * @param n + * @return + */ + public static double myPow3(double x, int n) { + long N = n; + if (N < 0) { + // 当n为负数的情况下, 实际上就是 1/x 的n次方 + x = 1 / x; + N = -N; + } + double half = 1; + // 循环 n / 2次, 这里如果n为偶数, 则结果为n的一半, 如果是奇数, 结果为n-1的一半 + for (long i = 0; i < N / 2; i++) + half = half * x;// 将x乘以自身 n/2 次 + + // 如果n是偶数 + if (n % 2 == 0) { + half = half * half; //直接temp的平方即可 + } else { + // 如果n为奇数 + half = half * half * x; // temp的平方还需要乘以x + } + return half; + } + + + /** + * 使用递归 + 分治法, 时间复杂度O(logn), 每一次我们使用公式 x的n/2次, n都为原来的一半, 所以之多O(log n)次即可求出结果 + * + * @param x + * @param n + * @return + */ + public static double myPowRecursion(double x, int n) { + // 处理n为负数的情况 + long N = n; + if (n < 0) { + x = 1 / x; + N = -N; + } + return fastPow(x, N); + } + + public static double fastPow(double x, long n) { + // 当n/2 不断递归除尽后, n==0, 返回1.0 + if (n == 0) { + System.out.println("往内递归结束 n= " + n); + return 1.0; + } + System.out.println("往内递归 n= " + n); + // 使用递归, 将循环乘以x的次数减少到logn次 (类似于二分查找) + double half = fastPow(x, n / 2);//每次递归的n都是上一次的1/2 + double beforeHalf = half; + // 需要考虑n为偶数和奇数的情况 + if (n % 2 == 0) { + half = half * half; + System.out.println(String.format("往外递归 n = %d, half = %s * %s = %s", n, beforeHalf, beforeHalf, half)); + } else { + half = half * half * x; + System.out.println(String.format("往外递归 n = %d, half = %s * %s * %s = %s", n, beforeHalf, beforeHalf, x, half)); + } + return half; + } + + + /** + * 暴利法(递归) 利用递归的方式将 x乘以自己n次, 效果和迭代一样. 时间复杂度O(n) + * @param x + * @param n + * @return + */ + public static double myPowRecursion2(double x, int n) { + // 处理n为负数的情况 + long N = n; + if (n < 0) { + x = 1 / x; + N = -N; + } + return slowPow(x, N); + } + + public static double slowPow(double x, long n){ + if(n == 0){ + return 1.0; + } + + // 从外往内 找终止条件 + double result = slowPow(x , n-1); + // 从内往外递归 真正开始计算 + result *= x; + + return result; + } +} From b5c769f794986cc06cd89960f6fbfb73eac252e3 Mon Sep 17 00:00:00 2001 From: longwu Date: Fri, 10 Jan 2020 00:06:44 +0800 Subject: [PATCH 14/74] =?UTF-8?q?=E5=A4=9A=E6=95=B0=E5=85=83=E7=B4=A0,=20?= =?UTF-8?q?=E4=B9=9F=E5=8F=AB=E4=BC=97=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/study/number/MajorityElement.java | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 src/main/java/com/study/number/MajorityElement.java diff --git a/src/main/java/com/study/number/MajorityElement.java b/src/main/java/com/study/number/MajorityElement.java new file mode 100644 index 0000000..e3cfab7 --- /dev/null +++ b/src/main/java/com/study/number/MajorityElement.java @@ -0,0 +1,244 @@ +package com.study.number; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * 多数元素, 也叫众数 + *

+ * 给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。 + *

+ * 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 + *

+ * 示例 1: + *

+ * 输入: [3,2,3] + * 输出: 3 + * 示例 2: + *

+ * 输入: [2,2,1,1,1,2,2] + * 输出: 2 + *

+ * 链接:https://leetcode-cn.com/problems/majority-element + */ +public class MajorityElement { + public static void main(String[] args) { + //int[] nums = {3, 2, 3}; + //int[] nums = {1}; + //int[] nums = {8, 8, 7, 7, 7}; + int[] nums = {8, 1, 2, 7, 7}; + //System.out.println(majorityElementByDoubleLoop(nums)); + //System.out.println(majorityElementByHashMap(nums)); + //System.out.println(majorityElementBySort(nums)); + //System.out.println(majorityElementByRecursion(nums)); + System.out.println(majorityElementByBoyerMoore(nums)); //投票算法 + } + + /** + * 暴力解法, 循环两次, 第一次循环每个元素, 第二次循环统计当前元素在整个数组中出现的个数,并判断是否大于数组长度的1/2,如果大于就返回该元素. + *

+ * 时间复杂度为 O(n2) + * + * @param nums + * @return + */ + private static int majorityElementByDoubleLoop(int[] nums) { + int count = 1; + + // 边界处理 + if (nums.length == 1) { + return nums[0]; + } + + // 循环每一个元素 + for (int i = 0; i < nums.length; i++) { + // 将当前元素 与其他元素做对比, 如果相同次数就+1, 一旦达到数组长度1/2就返回该元素 + for (int j = 0; j < nums.length; j++) { + if (i != j && nums[i] == nums[j]) { + count++; + if (count > nums.length / 2) + return nums[i]; + } + } + // 循环到下一个元素后,计数器需要还原 + count = 1; + } + return -1; + } + + /** + * 使用hashmap存储每个元素的数量, 由于hashmap读写的时间复杂度为O(1), 主要时间花费在一层循环上, + *

+ * 时间复杂度为O(n) + * + * @param nums + * @return + */ + private static int majorityElementByHashMap(int[] nums) { + Map map = new HashMap<>(); + + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(nums[i])) + map.put(nums[i], map.get(nums[i]) + 1); + else + map.put(nums[i], 1); + + // 放完元素后 计算一下该元素个数是否大于数组长度的一半 + Integer count = map.get(nums[i]); + if (count != null && count > nums.length / 2) { + return nums[i]; + } + } + + return -1; + } + + /** + * 对数组进行排序, 排完序后, 如果题目说一定存在某个元素个数大于长度的一半,那么这个元素肯定会在数组的中间位置. + * + * 时间复杂度为O(nlogn) + * + * @param nums + * @return + */ + private static int majorityElementBySort(int[] nums) { + + // 对数组进行排序, 时间复杂度为O(nlogn) + Arrays.sort(nums); + + // 如果题目说肯定有元素超过数组长度的一半 + return nums[nums.length / 2]; //那排序后 这个元素肯定会处在1/2的位置 + } + + + /** + * 对数组进行排序, 排完序后, 记录每个元素的个数. + * 对于排序后的元素, 可以直接进行一次遍历,然后对每个元素进行count[num]++,以及判断元素个数 + *

+ * 由于排序,所以最快的时间复杂度也要O(nlogn) + * + * @param nums + * @return + */ + private static int majorityElementBySort2(int[] nums) { + + // 边界处理 + if (nums.length == 1) + return nums[0]; + + // 对数组进行排序, 时间复杂度为O(nlogn) + Arrays.sort(nums); + + int count = 1; + int lastNum = nums[0]; + + // 循环部分时间复杂度为O(n) + // 从第2个元素开始循环 + for (int i = 1; i < nums.length; i++) { + // 如果当前元素和上一个元素相等, 那么出现次数+1 + if (nums[i] == lastNum) + count++; + else //否则次数恢复为1 + count = 1; + + // 修改上一个元素 + lastNum = nums[i]; + + // 如果该元素出现次数大于数组的一半,直接返回 + if (count > nums.length / 2) + return nums[i]; + } + + return -1; + } + + + private static int countInRange(int[] nums, int num, int low, int high) { + int count = 0; + for (int i = low; i <= high; i++) { + if (nums[i] == num) { + count++; + } + } + return count; + } + + /** + * 利用递归 + 分治的方式,将数组分成左右两个数组,分别从两个数组中获取众数, 如果两边众数相等,则直接返回该众数. + * + * 如果众数不相等, 则计算两个众数的个数, 取多的那个. + * + * 时间复杂度 O(nlogn) + * + * @param nums + * @param low + * @param high + * @return + */ + private static int majorityElementRecursion(int[] nums, int low, int high) { + // 只有一个元素的情况 + if (low == high) { + // 递归终止条件 + System.out.println(String.format("从外往内递归终止: high = %d, low = %d, result = %d", high, low, nums[low])); + return nums[low]; + } + + // 将数组分成左右两边两部分, 分别进行递归 + int mid = (high - low) / 2 + low; + System.out.println(String.format("从外往内递归: high = %d, low = %d, mid = %d", high, low, mid)); + int left = majorityElementRecursion(nums, low, mid); + int right = majorityElementRecursion(nums, mid + 1, high); + + System.out.println(String.format("从内往外递归: left = %d, right = %d",left, right)); + + // 如果左右两边的众数一样,那该众数即为数组的众数 + if (left == right) { + return left; + } + + // 如果左右众数不一样, 那么分别计算两边众数的个数 ,多的为整个数组的众数 + int leftCount = countInRange(nums, left, low, high); + int rightCount = countInRange(nums, right, low, high); + + return leftCount > rightCount ? left : right; + } + + public static int majorityElementByRecursion(int[] nums) { + return majorityElementRecursion(nums, 0, nums.length - 1); + } + + /** + * Boyer-Moore 投票算法 + * + * 原理: 众数肯定比非众数多, 所以众数和非众数抵消后,剩下的必然是众数 + * + * 该算法时间复杂度为O(n),空间复杂度为O(1),只需要对原数组进行两趟扫描,并且简单易实现。第一趟扫描我们得到一个候选节点candidate,第二趟扫描我们判断candidate出现的次数是否大于⌊ n/2 ⌋。 + * + * 第一趟扫描中,我们需要记录2个值: + * + * candidate,初值可以为任何数 + * count,初值为0 + * 之后,对于数组中每一个元素,首先判断count是否为0,若为0,则把candidate设置为当前元素。之后判断candidate是否与当前元素相等,若相等则count+=1,否则count-=1。 + * 原文链接:https://blog.csdn.net/kimixuchen/article/details/52787307 + * + * @param nums + * @return + */ + public static int majorityElementByBoyerMoore(int[] nums) { + int count = 0; + // 众数 + Integer candidate = null; + + for (int num : nums) { + // 如果count变回了0, 相当于众数和非众数互相抵消, 则后面的候选数(众数)需要重新选取 + if (count == 0) { + candidate = num; //只有count为0的时候 才重新选取候选数(众数) + } + // 如果新的数字为众数, 则count+1, 否则count-1 + count += (num == candidate) ? -1 : 1; + } + + return candidate; + } +} From 9be91f6fca805e74be0c01f7836e1847f2e577b9 Mon Sep 17 00:00:00 2001 From: longwu Date: Sat, 11 Jan 2020 15:47:46 +0800 Subject: [PATCH 15/74] =?UTF-8?q?=E6=96=90=E6=B3=A2=E6=8B=89=E5=A5=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dynamicprogramming/FibonacciSequence.java | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 src/main/java/com/study/dynamicprogramming/FibonacciSequence.java diff --git a/src/main/java/com/study/dynamicprogramming/FibonacciSequence.java b/src/main/java/com/study/dynamicprogramming/FibonacciSequence.java new file mode 100644 index 0000000..c352373 --- /dev/null +++ b/src/main/java/com/study/dynamicprogramming/FibonacciSequence.java @@ -0,0 +1,113 @@ +package com.study.dynamicprogramming; + +/** + * 斐波拉契 + * 斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”, + * 指的是这样一个数列:1、1、2、3、5、8、13、21、34、…… + *

+ * 临界值: 第1和2个数 + * + *

+ * https://www.jianshu.com/p/25ca6afa2fa4 + * https://www.cnblogs.com/swfpt/p/6850396.html + */ +public class FibonacciSequence { + + public static void main(String[] args) { + int n = 1; + //System.out.println(String.format("第%d个数为: %d", n, fibByRecursion(n)); + //System.out.println(String.format("第%d个数为: %d", n, fibByLoop(n))); + System.out.println(String.format("第%d个数为: %d", n, fibByDP(n))); + + n = 5; + //System.out.println(String.format("第%d个数为: %d", n, fibByRecursion(n)); + //System.out.println(String.format("第%d个数为: %d", n, fibByLoop(n))); + System.out.println(String.format("第%d个数为: %d", n, fibByDP(n))); + n = 6; + //System.out.println(String.format("第%d个数为: %d", n, fibByRecursion(n)); + //System.out.println(String.format("第%d个数为: %d", n, fibByLoop(n))); + System.out.println(String.format("第%d个数为: %d", n, fibByDP(n))); + } + + /** + * 求第n个数的值, 使用分治的方法 使用两个递归分别求出n-1和n-2两数的值,然后进行相加得到第n个数的值 + *

+ * 缺点: 当n比较大时递归非常慢,因为递归过程中存在很多重复计算。 + *

+ * 时间复杂度为O(2^n) + * + * @param n + * @return + */ + private static int fibByRecursion(int n) { + if (n == 0) + return 0; + + // 斐波拉契的第1和第2个数为1 + // 临界条件 n = 1 和 n=2 + if (n == 1 || n == 2) { + System.out.println(String.format("往内递归结束:第%d个数", n)); + return 1; + } + System.out.println(String.format("往内递归,第%d个数", n)); + int result = fibByRecursion(n - 1) + fibByRecursion(n - 2); //第n个数为前两个数之后 比如 5 = 3 +2 + System.out.println(String.format("往外递归计算开始: 第%d个数为%d", n, result)); + return result; + } + + /** + * 使用迭代的方式, 保存之前计算的结果, 用空间换时间 + *

+ * 时间复杂度为 O(n), 相比递归要快很多 + * + * @return + */ + private static int fibByLoop(int n) { + + if (n == 0) + return 0; + + if (n == 1 || n == 2) + return 1; + + int f1 = 1; + int f2 = 1; + // 1 1 2 3 5 8 ... + // 从第3个元素开始计算,因为前两个元素值已知 + for (int i = 3; i <= n; i++) { + int f3 = f1 + f2; // 记录当前两个元素之和,为下轮的第2个元素 + // 每一次循环都将当前两个元素往后移动一位 + f1 = f2;// 下轮的第1个元素为本轮的第2个元素 + f2 = f3; // 下轮的第2个元素为本轮的第3个元素 + System.out.println(String.format("fibByLoop:第%d个数为%d", i, f2)); + } + return f2; + } + + /** + * 使用斐波拉契来计算动态规划, 使用一个数组来存储整个斐波拉契数列, 这样的做法可以节省大量中间变量的创建, 而且时间复杂度和循环一样 + *

+ * 时间复杂度为O(n), 空间复杂度为O(n) + * + * @return + */ + private static int fibByDP(int n) { + if (n == 0) + return 0; + + if (n == 1 || n == 2) + return 1; + + int[] arr = new int[n]; + + arr[0] = 1; + arr[1] = 1; + + for (int i = 2; i < n; i++) { + arr[i] = arr[i - 1] + arr[i - 2]; + System.out.println(String.format("fibByDP:第%d个数为%d", i + 1, arr[i])); + } + + return arr[n - 1]; + } +} From e0240566b17254d993e7464cc86c9c93c35ab124 Mon Sep 17 00:00:00 2001 From: longwu Date: Sat, 11 Jan 2020 16:57:35 +0800 Subject: [PATCH 16/74] =?UTF-8?q?=E5=8A=A8=E6=80=81=E8=A7=84=E5=88=92?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20-=20=E7=88=AC=E6=A5=BC=E6=A2=AF=203?= =?UTF-8?q?=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dynamicprogramming/ClimbingStairs.java | 70 ---------- .../ClimbingStairs3Steps.java | 127 ++++++++++++++++++ 2 files changed, 127 insertions(+), 70 deletions(-) delete mode 100644 src/main/java/com/study/dynamicprogramming/ClimbingStairs.java create mode 100644 src/main/java/com/study/dynamicprogramming/ClimbingStairs3Steps.java diff --git a/src/main/java/com/study/dynamicprogramming/ClimbingStairs.java b/src/main/java/com/study/dynamicprogramming/ClimbingStairs.java deleted file mode 100644 index 97e32dd..0000000 --- a/src/main/java/com/study/dynamicprogramming/ClimbingStairs.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.study.dynamicprogramming; - -/** - * 动态规划问题 - 爬楼梯 - *

- * 问题: 有n层台阶, 每次可以走1层或2层或3层, 求有多少种走法 - *

- * 可以通过回溯法或者动态规划来解 - *

- *

- * 台阶数 --> 走法数量 - * 1 --> 1 - * 2 --> 11 2 - * 3 --> 111 12 21 3 - *

- * https://time.geekbang.org/course/detail/130-69779 - */ -public class ClimbingStairs { - - public static void main(String[] args) { - System.out.println(climbStairs2(3)); - System.out.println(climbStairs2(4)); - System.out.println(climbStairs2(5)); - - System.out.println(climbStairs(3)); - System.out.println(climbStairs(4)); - System.out.println(climbStairs(5)); - } - - private static int climbStairs(int n) { - // 第0-3层的走法 已经知道 - if (n == 0) - return 0; - if (n == 1 || n == 2) - return 3; - if (n == 3) - return 4; - - int f1 = 1; - int f2 = 2; - int f3 = 4; - - int result = 0; - // 从第4层开始计算 - for (int i = 4; i <= n; i++) { - result = f1 + f2 + f3; - f1 = f2; - f2 = f3; - f3 = result; - } - - return result; - } - - /** - * 使用递归的解法 - * - * @param n - * @return - */ - private static int climbStairs2(int n) { - if (n == 0 || n == 1 || n == 2) { - return n; - } else if (n == 3) { - return 4; - } - - return climbStairs2(n - 1) + climbStairs2(n - 2) + climbStairs2(n - 3); - } -} diff --git a/src/main/java/com/study/dynamicprogramming/ClimbingStairs3Steps.java b/src/main/java/com/study/dynamicprogramming/ClimbingStairs3Steps.java new file mode 100644 index 0000000..1d1903b --- /dev/null +++ b/src/main/java/com/study/dynamicprogramming/ClimbingStairs3Steps.java @@ -0,0 +1,127 @@ +package com.study.dynamicprogramming; + +/** + * 动态规划问题 - 爬楼梯 3层 + *

+ * 问题: 有n层台阶, 每次可以走1层或2层或3层, 求有多少种走法 + *

+ * 可以通过回溯法或者动态规划来解 + *

+ *

+ * 台阶数 --> 走法数量 + * 1 --> 1 + * 2 --> 11 2 + * 3 --> 111 12 21 3 + *

+ * https://time.geekbang.org/course/detail/130-69779 + */ +public class ClimbingStairs3Steps { + + public static void main(String[] args) { + System.out.println(climbStairsByRecursion(3)); + System.out.println(climbStairsByRecursion(4)); + System.out.println(climbStairsByRecursion(5)); + + System.out.println(climbStairsByLoop(3)); + System.out.println(climbStairsByLoop(4)); + System.out.println(climbStairsByLoop(5)); + + // 递归走法 + System.out.println(climbStairsByRecursion2(5)); + + //动态规划走法 + System.out.println(climbStairsByArray(5)); + } + + /** + * 使用迭代的解法 + *

+ * 时间复杂度为O(n) + * + * @param n + * @return + */ + private static int climbStairsByLoop(int n) { + // 第0-3层的走法 已经知道 + if (n == 0 || n == 1 || n == 2) + return n; + if (n == 3) + return 4; + + int f1 = 1; + int f2 = 2; + int f3 = 4; + + int result = 0; + // 从第4层开始计算 + for (int i = 4; i <= n; i++) { + result = f1 + f2 + f3; + f1 = f2; + f2 = f3; + f3 = result; + } + + return result; + } + + /** + * 动态规划的解法, 使用数组来存储每n层可以走的方式 + * + * @param n + * @return + */ + private static int climbStairsByArray(int n) { + // 临界条件 + // 第1层和第2层各有1和2种走法 + if (n == 0 || n == 1 || n == 2) + return n; + // 第3层有4种走法 + if (n == 3) + return 4; + + // 之后的每层走法数为前面3层的走法之和 + int[] arr = new int[n]; + arr[0] = 1; + arr[1] = 2; + arr[2] = 4; + + // 从第4个层开始计算 + for (int i = 3; i < n; i++) { + // 将前3个元素相加得到第4个元素 + // 此外下一轮循环由于i+1了,所以这轮的i自动变成了下一轮的i-1,这轮的i-1变成了下一轮的i-2... + arr[i] = arr[i - 1] + arr[i - 2] + arr[i - 3]; + } + + // 返回第n个元素, 因为数组是从第0个元素开始的 + return arr[n - 1]; + } + + /** + * 使用递归的解法 + * + * @param n + * @return + */ + private static int climbStairsByRecursion(int n) { + // 如果是0,1,2层楼梯, 走法都刚好为层数 + if (n == 0 || n == 1 || n == 2) { + return n; + // 如果是3层楼梯,走法为4种 + } else if (n == 3) { + return 4; + } + + return climbStairsByRecursion(n - 1) + climbStairsByRecursion(n - 2) + climbStairsByRecursion(n - 3); + } + + + private static int climbStairsByRecursion2(int n) { + if (n == 0 || n == 1 || n == 2) { + return n; + } else if (n == 3) { + return 4; + } + + return climbStairsByRecursion2(n - 1) + climbStairsByRecursion2(n - 2) + climbStairsByRecursion2(n - 3); + } +} From cfea3eafdaf221e873f59ee9f4896a579bbccf6c Mon Sep 17 00:00:00 2001 From: longwu Date: Sat, 11 Jan 2020 17:16:52 +0800 Subject: [PATCH 17/74] =?UTF-8?q?=E5=8A=A8=E6=80=81=E8=A7=84=E5=88=92?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20-=20=E7=88=AC=E6=A5=BC=E6=A2=AF=202?= =?UTF-8?q?=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ClimbingStairs2Steps.java | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/main/java/com/study/dynamicprogramming/ClimbingStairs2Steps.java diff --git a/src/main/java/com/study/dynamicprogramming/ClimbingStairs2Steps.java b/src/main/java/com/study/dynamicprogramming/ClimbingStairs2Steps.java new file mode 100644 index 0000000..6da67f6 --- /dev/null +++ b/src/main/java/com/study/dynamicprogramming/ClimbingStairs2Steps.java @@ -0,0 +1,84 @@ +package com.study.dynamicprogramming; + +/** + * 动态规划问题 - 爬楼梯 + *

+ * 问题: 有n层台阶, 每次可以走1层或2层 求有多少种走法 + *

+ * 和斐波拉契数列是相同的解法 f(n) = f(n-1) + f(n-2) + */ +public class ClimbingStairs2Steps { + + public static void main(String[] args) { + + int n =5; + System.out.println(calcStepsByRecursion(n)); + System.out.println(calcStepsByLoop(n)); + System.out.println(calcStepsByArray(n)); + } + + /** + * 使用递归的方式进行计算 f(n) = f(n-1) + f(n-2) + * 缺点: 重复创建了大量元素 且每一层计算都需要递归2次 + *

+ * 时间复杂度为O(2^n) + * + * @param n + * @return + */ + private static int calcStepsByRecursion(int n) { + if (n == 0 || n == 1 || n == 2) + return n; + + return calcStepsByRecursion(n - 1) + calcStepsByRecursion(n - 2); + } + + /** + * 使用迭代的方式, 时间复杂都为O(n), 但依旧创建了大量重复变量 + * @param n + * @return + */ + private static int calcStepsByLoop(int n) { + if (n == 0 || n == 1 || n == 2) + return n; + + int f1 = 1; + int f2 = 2; + int f3 = 0; + + // 从第3层开始计算, 每层结果为前两层之和 + for (int i = 2; i < n; i++) { + f3 = f2 + f1; + f1 = f2; + f2 = f3; + } + return f3; + } + + /** + * 使用动态规划的方式, 数组来存储每个元素,节省了大量临时变量的创建 + * + * 时间复杂度为O(n) + * + * @param n + * @return + */ + private static int calcStepsByArray(int n) { + if (n == 0 || n == 1 || n == 2) + return n; + + // 创建n个元素的数组 + int[] arr = new int[n]; + + arr[0] = 1; + arr[1] = 2; + + for (int i = 2; i < n; i++) { + arr[i] = arr[i - 1] + arr[i - 2]; + } + + // 返回第n个元素, 数组是从0开始的 + return arr[n - 1]; + } + +} From cab0dfeabc3e5d6b1a5ce603d45b9fbca9e9154e Mon Sep 17 00:00:00 2001 From: longwu Date: Sat, 11 Jan 2020 23:57:19 +0800 Subject: [PATCH 18/74] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=B8=80=E4=B8=AALRU?= =?UTF-8?q?=20cache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/cache/LRUCacheClient.java | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 src/main/java/com/study/cache/LRUCacheClient.java diff --git a/src/main/java/com/study/cache/LRUCacheClient.java b/src/main/java/com/study/cache/LRUCacheClient.java new file mode 100644 index 0000000..e2f7a67 --- /dev/null +++ b/src/main/java/com/study/cache/LRUCacheClient.java @@ -0,0 +1,224 @@ +package com.study.cache; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * LRU least Recently Used 最近最少使用, 是一种淘汰策略,选择最久未使用的元素进行淘汰 + * + * 运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。 + *

+ * 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 + * 写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。 + *

+ * 进阶: + *

+ * 你是否可以在 O(1) 时间复杂度内完成这两种操作? + *

+ * 示例: + * LRUCache cache = new LRUCache(2); //2为缓存容量 + * cache.put(1, 1); + * cache.put(2, 2); + * cache.get(1); // 返回 1 + * cache.put(3, 3); // 该操作会使得密钥 2 作废 + * cache.get(2); // 返回 -1 (未找到) + * cache.put(4, 4); // 该操作会使得密钥 1 作废 + * cache.get(1); // 返回 -1 (未找到) + * cache.get(3); // 返回 3 + * cache.get(4); // 返回 4 + *

+ * 链接:https://leetcode-cn.com/problems/lru-cache + */ +public class LRUCacheClient { + public static void main(String[] args) { + LRUCache cache = new LRUCache(2); //2为缓存容量 + // 元素的热程度为最新放的最热, 下面是2最热 + cache.put(1, 1); + cache.put(2, 2); + // 获取key为1的值, 获取后 key为1的节点由于会到链表的尾部,变成最热的节点(不被淘汰的节点) + System.out.println("cache.get(1): " + cache.get(1)); // 返回 1 + // 之后再将一个key为3的元素放入缓存中, 放完之后3的热度比1高 + // 由于缓存只能放2个元素,所以 + cache.put(3, 3); // 由于key为2的节点没有被访问过, 所以该操作会使得key为2的元素 作废 + System.out.println("cache.get(2): " + cache.get(2)); // 由于key为2的元素被淘汰了,所以返回-1 // 返回 -1 + // 再放入一个key为4的元素, 放完之后由于空间限制只能保留2个元素,所以将最不热的1给淘汰了 + cache.put(4, 4); // 该操作会使得密钥 1 作废 + cache.get(1); // 返回 -1 (未找到) + cache.get(3); // 返回 3 + cache.get(4); // 返回 4 + } +} + +/** + * 方法一:使用LinkedHashMap实现, LinkedHashMap是一个有序的hashmap, 可以对内部的节点进行排序 + * 在调用put和get方法会执行afterNodeInsertion()和afterNodeAccess()方法, 将当前操作的节点放到链表的头部,成为最热的节点(最不容易被删除的节点), + * 同时移除最老的元素(链表尾部的元素),一旦容量不够的情况下 + *

+ * 时间复杂度:对于 put 和 get 都是 O(1)O(1)。 + * 空间复杂度:O(capacity)O(capacity),因为哈希表和双向链表最多存储 capacity + 1 个元素。 + *

+ * 链接:https://leetcode-cn.com/problems/lru-cache/solution/lru-huan-cun-ji-zhi-by-leetcode/ + */ +class LRUCache extends LinkedHashMap { + private int capacity; + + public LRUCache(int capacity) { + super(capacity, 0.75f, true); + this.capacity = capacity; + } + + /** + * 获取key的值,如果没有返回-1, 访问后,该key的节点会被放到头部,变成最热的节点(最不容易被删除的节点) + * + * @param key + * @return + */ + public int get(int key) { + return super.getOrDefault(key, -1); + } + + /** + * 插入键值对 + * + * @param key + * @param value + */ + public void put(int key, int value) { + super.put(key, value); + } + + /** + * 移除最老元素的策略是容量不够 + * + * @param eldest + * @return + */ + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return super.size() > this.capacity; + } +} + + +class LRUCache2{ + class DLinkedNode { + int key; + int value; + DLinkedNode prev; + DLinkedNode next; + } + + /** + * 添加新节点, 将新节点放到链表头部 + * @param node + */ + private void addNode(DLinkedNode node) { + /** + * Always add the new node right after head. + */ + node.prev = head; + node.next = head.next; + + head.next.prev = node; + head.next = node; + } + + /** + * 删除一个节点 + * + * @param node + */ + private void removeNode(DLinkedNode node){ + /** + * Remove an existing node from the linked list. + */ + DLinkedNode prev = node.prev; + DLinkedNode next = node.next; + + prev.next = next; + next.prev = prev; + } + + /** + * 将当前节点放到链表头部 + * + * @param node + */ + private void moveToHead(DLinkedNode node){ + /** + * Move certain node in between to the head. + */ + removeNode(node); + addNode(node); + } + + /** + * 弹出尾部节点 + * @return + */ + private DLinkedNode popTail() { + /** + * Pop the current tail. + */ + DLinkedNode res = tail.prev; + removeNode(res); + return res; + } + + private HashMap cache = new HashMap(); + private int size; + private int capacity; + private DLinkedNode head, tail; + + public LRUCache2(int capacity) { + this.size = 0; + this.capacity = capacity; + + head = new DLinkedNode(); + // head.prev = null; + + tail = new DLinkedNode(); + // tail.next = null; + + head.next = tail; + tail.prev = head; + } + + public int get(int key) { + DLinkedNode node = cache.get(key); + if (node == null) return -1; + + // 将当前节点放到链表头部 + moveToHead(node); + + return node.value; + } + + public void put(int key, int value) { + DLinkedNode node = cache.get(key); + + if(node == null) { + DLinkedNode newNode = new DLinkedNode(); + newNode.key = key; + newNode.value = value; + + cache.put(key, newNode); + addNode(newNode); + + ++size; + + if(size > capacity) { + // 从链表中删除尾部节点,同时从hashmap中删除 + DLinkedNode tail = popTail(); + cache.remove(tail.key); + --size; + } + } else { + // update the value. + node.value = value; + // 更新节点 也会将节点放到链表头部 + moveToHead(node); + } + } +} \ No newline at end of file From 29276df728aa39685d190fa7264350f9f23c4d28 Mon Sep 17 00:00:00 2001 From: longwu Date: Sun, 12 Jan 2020 13:47:45 +0800 Subject: [PATCH 19/74] =?UTF-8?q?=E4=B9=B0=E5=8D=96=E8=82=A1=E7=A5=A8-?= =?UTF-8?q?=E8=B4=AA=E5=BF=83=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../number/BestTimeToBuyAndSellStockII.java | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/main/java/com/study/number/BestTimeToBuyAndSellStockII.java diff --git a/src/main/java/com/study/number/BestTimeToBuyAndSellStockII.java b/src/main/java/com/study/number/BestTimeToBuyAndSellStockII.java new file mode 100644 index 0000000..da053e3 --- /dev/null +++ b/src/main/java/com/study/number/BestTimeToBuyAndSellStockII.java @@ -0,0 +1,114 @@ +package com.study.number; + +/** + * 买卖股票的最佳时机 II + *

+ * 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 + *

+ * 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。 + *

+ * 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 + *

+ * 示例 1: + *

+ * 输入: [7,1,5,3,6,4] + * 输出: 7 + * 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 + *   随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。 + * 示例 2: + *

+ * 输入: [1,2,3,4,5] + * 输出: 4 + * 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 + *   注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。 + *   因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 + * 示例 3: + *

+ * 输入: [7,6,4,3,1] + * 输出: 0 + * 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 + *

+ * 链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii + */ +public class BestTimeToBuyAndSellStockII { + + /** + * 同一支股票卖完还能再买, 也能买了再卖. + * 但是不能连续买两次(不卖) + * + * 可以通过贪心法,波峰法以及暴力法来解答 + * + * @param args + */ + public static void main(String[] args) { + int[] prices = {7, 1, 5, 3, 6, 4}; + //System.out.println(maxProfit(prices)); + System.out.println(maxProfit2(prices)); + + int[] prices2 = {1, 2, 3, 4, 5}; + //System.out.println(maxProfit(prices2)); + System.out.println(maxProfit2(prices2)); + + int[] prices3 = {7, 6, 4, 3, 1}; + //System.out.println(maxProfit(prices3)); + System.out.println(maxProfit2(prices3)); + } + + + /** + * 使用贪心算法, 每支股票的利润都不放过 + * + * 循环遍历,支要下一个股票比当前股票贵,我就买当前的,然后卖下一个,获取下一个和当前之间的利润 + * + * 时间复杂度O(n) 空间复杂度O(1) + * + * @param prices + * @return + */ + public static int maxProfit(int[] prices) { + int profit = 0; + for (int i = 0; i < prices.length; i++) { + // 前一个数小于后一个就买, 因为先买再卖能赚 + if (prices.length > i + 1 && prices[i] < prices[i + 1]) { + profit += prices[i + 1] - prices[i]; + } + } + return profit; + } + + /** + * 峰谷法 + * + * 循环数组, 先找波谷(用于先买),再找波峰(用于后卖), 利润=波峰-波谷 + * + * 波谷为当前价格小于下一支股票, 波峰为当前价格大于下一支股票 + * + * 时间复杂度:O(n)。遍历一次。 + * + * 空间复杂度:O(1)。需要常量的空间。 + * + * @param prices + * @return + */ + public static int maxProfit2(int[] prices) { + int i = 0; + int valley = prices[0]; + int peak = prices[0]; + int maxprofit = 0; + // 遍历每个股票 + while (i < prices.length - 1) { + // 找波谷, 波谷为当前股票价格低于下一支股票 + while (i < prices.length - 1 && prices[i] >= prices[i + 1]) + i++; + valley = prices[i];// 记录波谷 + // 找波峰, 波峰为当前股票价格高于下一支股票 + while (i < prices.length - 1 && prices[i] <= prices[i + 1]) + i++; + peak = prices[i]; // 记录波峰 + + // 利润 = 波峰 - 波谷 + maxprofit += peak - valley; + } + return maxprofit; + } +} From 02aeb7664eaaa371caf5d62418250036f3e24a6d Mon Sep 17 00:00:00 2001 From: longwu Date: Sun, 12 Jan 2020 13:50:46 +0800 Subject: [PATCH 20/74] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=B3=B0=E8=B0=B7?= =?UTF-8?q?=E6=B3=95=E4=B8=AD=E8=82=A1=E7=A5=A8=E6=95=B0=E7=BB=84=E4=B8=BA?= =?UTF-8?q?=E7=A9=BA=E5=AF=BC=E8=87=B4=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/number/BestTimeToBuyAndSellStockII.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/study/number/BestTimeToBuyAndSellStockII.java b/src/main/java/com/study/number/BestTimeToBuyAndSellStockII.java index da053e3..4c7249f 100644 --- a/src/main/java/com/study/number/BestTimeToBuyAndSellStockII.java +++ b/src/main/java/com/study/number/BestTimeToBuyAndSellStockII.java @@ -92,8 +92,8 @@ public static int maxProfit(int[] prices) { */ public static int maxProfit2(int[] prices) { int i = 0; - int valley = prices[0]; - int peak = prices[0]; + int valley; + int peak; int maxprofit = 0; // 遍历每个股票 while (i < prices.length - 1) { From eff932b450489cf9be029827ba3df553f18888f0 Mon Sep 17 00:00:00 2001 From: longwu Date: Sun, 12 Jan 2020 16:06:48 +0800 Subject: [PATCH 21/74] =?UTF-8?q?BFS=E5=B9=BF=E5=BA=A6=E4=BC=98=E5=85=88?= =?UTF-8?q?=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/study/search/BFS.java | 106 ++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/main/java/com/study/search/BFS.java diff --git a/src/main/java/com/study/search/BFS.java b/src/main/java/com/study/search/BFS.java new file mode 100644 index 0000000..b29041f --- /dev/null +++ b/src/main/java/com/study/search/BFS.java @@ -0,0 +1,106 @@ +package com.study.search; + +import com.study.binarytree.TreeNode; +import com.study.utils.TreeUtils; + +import java.util.*; + +/** + * BFS 广度优先搜索 + * 按层级一层一层进行搜索 + * + * https://leetcode-cn.com/problems/binary-tree-level-order-traversal/ + */ +public class BFS { + + public static void main(String[] args) { + int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + List treeNodes = TreeUtils.buildTree(arr); + TreeNode root = treeNodes.get(0); + + List> visitedNodes = searchByLoop(root); + // List> visitedNodes = searchByRecursion(root); + for (List currentLevelNodes : visitedNodes) { + for(TreeNode node : currentLevelNodes){ + System.out.print(node.val + " "); + } + System.out.println(); + } + } + + /** + * 使用遍历的方式进行广度优先搜索, 需要借助一个Queue的结构,先进先出 + * + * 时间复杂度为O(n). 空间复杂度为O(n^2),用到了两层集合 + * + * @param root + * @return + */ + private static List> searchByLoop(TreeNode root) { + // 用于存储访问过的节点 + List> visited = new ArrayList<>(); + + if (root == null) + return visited; + + // 记录节点层级 + int level = 0; + + // 利用队列先进先出的特性,实现层级的从左往右遍历 + Queue nodes = new LinkedList<>(); //链表也是有序的,并且实现了队列的功能,先进先出 + nodes.offer(root); + + while (!nodes.isEmpty()) { + // 添加层 + visited.add(new ArrayList<>()); + // 获取当前层级的节点数 + int size = nodes.size(); + + // 遍历当前层级所有节点, 将他们的子节点从左往右加到队列中去 + for (int i = 0; i < size; i++) { + TreeNode node = nodes.poll(); //取出最先加入的节点, 并将其子节点从左往右加入到队列中, 用于下一轮遍历 + visited.get(level).add(node); //给当前层添加节点 + if (node.left != null) { + nodes.offer(node.left); + } + if (node.right != null) { + nodes.offer(node.right); + } + } + level++; + } + return visited; + } + + /** + * 使用递归的方式实现广度优先, 这里在递归的过程中用到了层级控制,如果集合当前层没有节点,就新建; 如果有节点, 就加到当前层里面 + * + * @param root + * @return + */ + private static List> searchByRecursion(TreeNode root) { + List> nodeList = new ArrayList<>(); + if (root == null) + return nodeList; + // 递归获取所有子节点 + search(root, 0, nodeList); + return nodeList; + } + + private static void search(TreeNode root, int level, List> nodeList) { + if (root == null) { + return; + } + // 集合的当前层没有节点, 就新建 + if (level >= nodeList.size()) { + List subList = new ArrayList<>(); + subList.add(root); + nodeList.add(subList); + } else { + // 集合的当前层有节点, 就直接添加 + nodeList.get(level).add(root); + } + search(root.left, level + 1, nodeList); + search(root.right, level + 1, nodeList); + } +} From 9ac5ec933e2e860f7f3808329da6a764809e88dd Mon Sep 17 00:00:00 2001 From: longwu Date: Sun, 12 Jan 2020 17:13:44 +0800 Subject: [PATCH 22/74] =?UTF-8?q?=E6=B7=B1=E5=BA=A6=E4=BC=98=E5=85=88?= =?UTF-8?q?=E9=81=8D=E5=8E=86dfs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/study/search/DFS.java | 77 +++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/main/java/com/study/search/DFS.java diff --git a/src/main/java/com/study/search/DFS.java b/src/main/java/com/study/search/DFS.java new file mode 100644 index 0000000..fd0a4df --- /dev/null +++ b/src/main/java/com/study/search/DFS.java @@ -0,0 +1,77 @@ +package com.study.search; + +import com.study.binarytree.TreeNode; +import com.study.utils.TreeUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * 深度优先搜索 + *

+ * 其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次. + *

+ * 常见的解法有: 递归(递归是天然的深度优先遍历法则) + */ +public class DFS { + + /** + * 深度优先遍历 和 二叉树的前序遍历(根 左 右)是一样的 + * + * @param args + */ + public static void main(String[] args) { + int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + List treeNodes = TreeUtils.buildTree(arr); + TreeNode root = treeNodes.get(0); + + List nodes = search(root); + for (TreeNode node : nodes) { + System.out.print(node.val + " "); + } + } + + /** + * 通过递归的方式实现深度优先遍历 + * + * @return + */ + private static List search(TreeNode root) { + List nodeList = new ArrayList<>(); + if (root == null) + return nodeList; + dfs(root, nodeList); + return nodeList; + } + + private static void dfs(TreeNode node, List nodeList) { + // 递归结束条件为节点为空 + if (node == null) { + return; + } + + // 将节点加到集合中, 因为有些节点会反复访问,所以这里需要去重 + if (!nodeList.contains(node)) { + nodeList.add(node); + //System.out.println("经过节点:" + node.val); + } else { + //System.out.println("经过重复节点:" + node.val); + } + + + // 先从左边从外往内递归 直到最深处, 当node为null的时候结束递归开始从内往外 + if (node.left != null) { + // 1 3 7 5 + System.out.println("递归左边: " + node.left.val); + dfs(node.left, nodeList); + } + + // 再从右边一直往内部 + // 左边从外往内直到node==null后终止往内, 开始由内往外, 再往外的过程中不断再由外往内递归右子树 + if (node.right != null) { + // 4 2 6 + System.out.println("递归右边: " + node.right.val); + dfs(node.right, nodeList); + } + } +} From 55ebffb97e6b49e8441b566fd4a866bbaec417aa Mon Sep 17 00:00:00 2001 From: longwu Date: Sun, 12 Jan 2020 17:15:42 +0800 Subject: [PATCH 23/74] =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91=E5=B1=82?= =?UTF-8?q?=E7=BA=A7=E5=A2=9E=E5=8A=A0=E6=B7=B1=E5=BA=A6=E4=BC=98=E5=85=88?= =?UTF-8?q?=E9=80=92=E5=BD=92=E7=9A=84=E8=A7=A3=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/study/binarytree/LevelTraversal.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/main/java/com/study/binarytree/LevelTraversal.java b/src/main/java/com/study/binarytree/LevelTraversal.java index 0b3a048..fab377e 100644 --- a/src/main/java/com/study/binarytree/LevelTraversal.java +++ b/src/main/java/com/study/binarytree/LevelTraversal.java @@ -122,4 +122,36 @@ private static List> levelOrder2(TreeNode root) { } return nodes; } + + /** + * 使用递归的方式实现广度优先, 这里在递归的过程中用到了层级控制,如果集合当前层没有节点,就新建; 如果有节点, 就加到当前层里面 + * + * @param root + * @return + */ + private static List> searchByRecursion(TreeNode root) { + List> nodeList = new ArrayList<>(); + if (root == null) + return nodeList; + // 递归获取所有子节点 + search(root, 0, nodeList); + return nodeList; + } + + private static void search(TreeNode root, int level, List> nodeList) { + if (root == null) { + return; + } + // 集合的当前层没有节点, 就新建 + if (level >= nodeList.size()) { + List subList = new ArrayList<>(); + subList.add(root); + nodeList.add(subList); + } else { + // 集合的当前层有节点, 就直接添加 + nodeList.get(level).add(root); + } + search(root.left, level + 1, nodeList); + search(root.right, level + 1, nodeList); + } } From 28b62117f6feca48c6ff681907ccadc7dec596e6 Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 13 Jan 2020 12:36:13 +0800 Subject: [PATCH 24/74] =?UTF-8?q?=E5=89=8D=E5=BA=8F=E9=81=8D=E5=8E=86?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96,=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E9=9B=86=E5=90=88=E7=9A=84=E6=96=B9=E5=BC=8F=E6=9D=A5=E5=A4=84?= =?UTF-8?q?=E7=90=86=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/binarytree/PreorderTraversal.java | 85 ++++++++++++++++++- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/study/binarytree/PreorderTraversal.java b/src/main/java/com/study/binarytree/PreorderTraversal.java index 8d424bb..fd147a0 100644 --- a/src/main/java/com/study/binarytree/PreorderTraversal.java +++ b/src/main/java/com/study/binarytree/PreorderTraversal.java @@ -33,15 +33,17 @@ public static void main(String[] args) { TreeNode root = treeNodes.get(0); //List list = preorderTraversal(root); - List list = preorderTraversal2(root); + //List list = preorderTraversalByLoop(root); + // List list = preorderTraversalByLoop2(root); + List list = preorderTraversalByLoopWithList(root); for (Integer num : list) { - System.out.println(num); + System.out.print(num + " "); } } /** - * 通过递归的方式遍历整个二叉树 + * 通过递归的方式遍历整个二叉树 时间复杂度O(n) *

* 前序遍历: 根 - 左 - 右 * @@ -65,11 +67,17 @@ public static List preorderTraversal(TreeNode root) { /** * 使用迭代的方式 遍历二叉树 + *

+ * 用到栈后入先出的原理, 将左子树放在右子树后面入栈, 确保左子树先弹出,优先处理左子树,之后再处理右子树 + *

+ * 关于为什么循环中没专门放入根节点, 因为除了最顶层的根节点, 其他层级的根节点都是左右子树 + *

+ * 时间复杂度O(n), 空间复杂度O(n) * * @param root * @return */ - public static List preorderTraversal2(TreeNode root) { + public static List preorderTraversalByLoop(TreeNode root) { List list = new ArrayList(); Stack stack = new Stack(); @@ -78,20 +86,89 @@ public static List preorderTraversal2(TreeNode root) { } stack.push(root); + System.out.println(String.format("节点%d入栈", root.val)); while (!stack.isEmpty()) { + // 每次优先弹出左节点, + // 由于每次左子树都是最后放进去,而每次循环都只pop一次, 存在左子树优先pop左子树,没有再pop右节点 TreeNode node = stack.pop(); + System.out.println(String.format("节点%d弹出", node.val)); list.add(node.val); // 根据栈后进先出的原理, 把left节点放在right后面放入栈中 if (node.right != null) { stack.push(node.right); + System.out.println(String.format("右节点%d入栈", node.right.val)); } if (node.left != null) { stack.push(node.left); + System.out.println(String.format("左节点%d入栈", node.left.val)); } } return list; } + + public static List preorderTraversalByLoop2(TreeNode root) { + List nodes = new ArrayList<>(); + + if (root == null) + return nodes; + + Stack stack = new Stack<>(); + stack.push(root); //将节点放到栈顶 + + while (!stack.isEmpty()) { + + // 弹出栈顶的节点 + TreeNode node = stack.pop(); + nodes.add(node.val); + + if (node.right != null) { + stack.push(node.right); + } + + // 根据后进先出的原理,将左子树节点放在后面入栈 + if (node.left != null) { + stack.push(node.left); + } + } + return nodes; + } + + /** + * 使用循环 + 集合 实现前序遍历 + * 每次都将节点放到集合顶部, 并且从集合顶部取出节点 + * + * @param root + * @return + */ + public static List preorderTraversalByLoopWithList(TreeNode root) { + List nodes = new ArrayList<>(); + + if (root == null) + return nodes; + + List list = new ArrayList<>(); + list.add(0, root); //将节点放到集合顶部 + + while (!list.isEmpty()) { + + // 取出并删除集合顶部元素 + TreeNode node = list.remove(0); + nodes.add(node.val); + + if (node.right != null) { + // 将右节点放到集合顶部 + list.add(0, node.right); + } + + // 根据后进先出的原理,将左子树节点放在后面入栈 + if (node.left != null) { + // 将左节点放到集合顶部 + list.add(0, node.left); + } + } + return nodes; + } } From 2a651b48b065d9c59e7f2de22dd03e19464a9571 Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 13 Jan 2020 22:21:30 +0800 Subject: [PATCH 25/74] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/binarytree/InorderTraversal.java | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/study/binarytree/InorderTraversal.java b/src/main/java/com/study/binarytree/InorderTraversal.java index a62c291..d55e87c 100644 --- a/src/main/java/com/study/binarytree/InorderTraversal.java +++ b/src/main/java/com/study/binarytree/InorderTraversal.java @@ -7,7 +7,7 @@ import java.util.Stack; /** - * 94. 二叉树的中序遍历 深度优先遍历dfs + * 94. 二叉树的中序遍历 *

* 给定一个二叉树,返回它的中序 遍历。 *

@@ -28,16 +28,16 @@ public class InorderTraversal { public static void main(String[] args) { - int[] arr = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - List treeNodes = TreeUtils.buildTree(arr); + List treeNodes = TreeUtils.buildTree(arr); TreeNode root = treeNodes.get(0); //List list = inorderTraversal(root); //List list = inorderTraversal2(root); List list = inorderTraversal3(root); for (Integer num : list) { - System.out.println(num); + System.out.print(num + " "); } } @@ -95,20 +95,28 @@ private static List inorderTraversal2(TreeNode root) { */ private static List inorderTraversal3(TreeNode root) { List list = new ArrayList(); + + if (root == null) + return list; + Stack stack = new Stack(); while (root != null || !stack.isEmpty()) { - // 左 + // 对于每个节点都将其所有左节点入栈 + // 如果当前节点不为空,递归其左节点 while (root != null) { - stack.push(root); + stack.push(root);//将左节点入栈 + System.out.println(String.format("节点%d被放入栈中",root.val)); root = root.left; } if (!stack.isEmpty()) { - // 根 (当左子节点为空的时候,该节点变为根) + // 将栈中的节点的第一个弹出 + // 存在左节点的,优先将最深层左节点弹出 root = stack.pop(); + System.out.println(String.format("节点%d从栈中弹出",root.val)); list.add(root.val); - // 右 + // 切到右子树,给下一轮找左子树使用 root = root.right; } } From cf8fb6926622dc1ee49943e663bae880e6bb44f3 Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 13 Jan 2020 22:52:20 +0800 Subject: [PATCH 26/74] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/binarytree/PostorderTraversal.java | 61 ++++++++++++++++--- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/study/binarytree/PostorderTraversal.java b/src/main/java/com/study/binarytree/PostorderTraversal.java index 050c2d9..336900c 100644 --- a/src/main/java/com/study/binarytree/PostorderTraversal.java +++ b/src/main/java/com/study/binarytree/PostorderTraversal.java @@ -7,7 +7,7 @@ import java.util.Stack; /** - * 145. 二叉树的后序遍历 深度优先遍历dfs + * 145. 二叉树的后序遍历 *

* 给定一个二叉树,返回它的 后序 遍历。 *

@@ -34,9 +34,13 @@ public static void main(String[] args) { //List list = postorderTraversal(root); //List list = postorderTraversal2(root); //List list = postorderTraversal3(root); - List list = postorderTraversal4(root); - for (Integer num : list) { - System.out.println(num); + // List list = postorderTraversal4(root); +// for (Integer num : list) { +// System.out.print(num + " "); +// } + Stack stack = postorderTraversal5(root); + while (!stack.isEmpty()) { + System.out.print(stack.pop() + " "); } } @@ -101,22 +105,61 @@ private static List postorderTraversal3(TreeNode root) { } stack.push(root); + System.out.println(String.format("将根节点%d入栈", root.val)); while (!stack.isEmpty()) { root = stack.pop(); + // 将节点值加到列表顶部 + list.add(0, root.val); + System.out.println(String.format("将节点%d加入到集合顶部", root.val)); + //和前序比那里不一样, 先将左节点入栈 - if (root.left != null) + if (root.left != null) { stack.push(root.left); + System.out.println(String.format("将左节点%d入栈", root.left.val)); + } + //再将右节点入栈 - if (root.right != null) + if (root.right != null) { stack.push(root.right); - - //逆序添加节点值 - list.add(0, root.val); + System.out.println(String.format("将右节点%d入栈", root.right.val)); + } } return list; } + private static Stack postorderTraversal5(TreeNode root) { + Stack result = new Stack(); + Stack stack = new Stack(); + + if (root == null) { + return result; + } + + stack.push(root); + System.out.println(String.format("将根节点%d入栈", root.val)); + + while (!stack.isEmpty()) { + root = stack.pop(); + // 将节点值加到列表顶部 + result.push(root.val); + System.out.println(String.format("将节点%d加入到栈中", root.val)); + + //和前序比那里不一样, 先将左节点入栈 + if (root.left != null) { + stack.push(root.left); + System.out.println(String.format("将左节点%d入栈", root.left.val)); + } + + //再将右节点入栈 + if (root.right != null) { + stack.push(root.right); + System.out.println(String.format("将右节点%d入栈", root.right.val)); + } + } + return result; + } + /** * 迭代遍历 左 右 根 *

From 74db564903e4462a543d1928c660e783c8370a7a Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 13 Jan 2020 22:54:39 +0800 Subject: [PATCH 27/74] =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91=E7=9A=84?= =?UTF-8?q?=E6=9C=80=E5=A4=A7=E6=B7=B1=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/study/binarytree/MaximumDepth.java | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 src/main/java/com/study/binarytree/MaximumDepth.java diff --git a/src/main/java/com/study/binarytree/MaximumDepth.java b/src/main/java/com/study/binarytree/MaximumDepth.java new file mode 100644 index 0000000..276982d --- /dev/null +++ b/src/main/java/com/study/binarytree/MaximumDepth.java @@ -0,0 +1,122 @@ +package com.study.binarytree; + +import com.study.utils.TreeUtils; + +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * 二叉树的最大深度 + *

+ * 给定一个二叉树,找出其最大深度。 + *

+ * 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 + *

+ * 说明: 叶子节点是指没有子节点的节点。 + *

+ * 示例: + * 给定二叉树 [3,9,20,null,null,15,7], + *

+ * 3 + * / \ + * 9 20 + * / \ + * 15 7 + * 返回它的最大深度 3 。 + *

+ * 链接:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree + */ +public class MaximumDepth { + + /** + * 可以通过深度优先进行递归 或者 层级遍历 + * @param args + */ + public static void main(String[] args) { + int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + List treeNodes = TreeUtils.buildTree(arr); + TreeNode root = treeNodes.get(0); + + //System.out.println(maxDepthByRecursion(root, "", root.val)); + System.out.println(maxDepthByLoop(root)); + System.out.println(maxDepthByRecursion2(root)); + } + + /** + * 使用dfs深度优先的策略(递归)的方式来解决这个问题 + *

+ * 从根递归到最底层叶子节点, 值为1, 然后每往上面一层+1,同时每层会对左右两边的值做对比,取最大的. 如果是算最大深度,那就是取根的左子树和右子树中的最大值 + *

+ * 我们每个结点只访问一次,因此时间复杂度为 O(N), + * + * @param root + * @return + */ + private static int maxDepthByRecursion(TreeNode root, String position, int val) { + if (root == null) { + System.out.println(String.format("%s从%d节点到叶子节点递归终止", position, val)); + return 0; + } + + System.out.println(String.format("%s从%d节点到叶子节点递归", position, val)); + + int left = maxDepthByRecursion(root.left, "左边", root.val); + // 左边递归到了叶子节点, 开始往根节点走 + System.out.println(String.format("左边从叶子节点往根节点开始, 当前节点为: %d left = %d", root.val, left)); + + int right = maxDepthByRecursion(root.right, "右边", root.val); + // 右边递归到了叶子节点, 开始往根节点走 + System.out.println(String.format("右边从叶子节点往根节点开始, 当前节点为: %d right = %d", root.val, right)); + + int result = Math.max(left, right) + 1; + System.out.println(String.format("result = %d, left = %d, right = %d", result, left, right)); + + return result; + } + + + private static int maxDepthByRecursion2(TreeNode root){ + if(root == null) + return 0; + + int left = maxDepthByRecursion2(root.left); + int right = maxDepthByRecursion2(root.right); + + return Math.max(left,right) + 1; + } + + + /** + * 使用广度优先的方式找出最大深度 遍历+队列 + *

+ * 需要将左右子树的最大深度进行对比, 求最大的 + * + * @param root + * @return + */ + private static int maxDepthByLoop(TreeNode root) { + if (root == null) + return 0; + + Queue queue = new LinkedList<>(); + queue.offer(root); + int level = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + level++; + } + // 返回最大层级数, 即最深层级 + return level; + } +} From 126d893f3a2e713b35fac7edd0c1e49eeff09c95 Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 13 Jan 2020 23:58:24 +0800 Subject: [PATCH 28/74] =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91=E6=9C=80?= =?UTF-8?q?=E5=B0=8F=E6=B7=B1=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/study/binarytree/MinimumDepth.java | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/main/java/com/study/binarytree/MinimumDepth.java diff --git a/src/main/java/com/study/binarytree/MinimumDepth.java b/src/main/java/com/study/binarytree/MinimumDepth.java new file mode 100644 index 0000000..84f9f05 --- /dev/null +++ b/src/main/java/com/study/binarytree/MinimumDepth.java @@ -0,0 +1,104 @@ +package com.study.binarytree; + +import com.study.utils.TreeUtils; +import sun.reflect.generics.tree.Tree; + +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * 二叉树的最小深度 + *

+ * 给定一个二叉树,找出其最小深度。 + *

+ * 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 + *

+ * 说明: 叶子节点是指没有子节点的节点。 + *

+ * 示例: + *

+ * 给定二叉树 [3,9,20,null,null,15,7], + *

+ * 3 + * / \ + * 9 20 + * / \ + * 15 7 + * 返回它的最小深度  2. + *

+ * 链接:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree + */ +public class MinimumDepth { + + public static void main(String[] args) { + int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + List treeNodes = TreeUtils.buildTree(arr); + TreeNode root = treeNodes.get(0); + + //System.out.println(minDepthByRecursion(root)); + System.out.println(minDepthByLoop(root)); + } + + /** + * 使用分治的方法 从左右节点进行递归 + * 时间复杂度为O(n) + * + * @param root + * @return + */ + private static int minDepthByRecursion(TreeNode root) { + if (root == null) + return 0; + // 使用分治的方法 从左右节点进行递归 + int left = minDepthByRecursion(root.left); + int right = minDepthByRecursion(root.right); + + // 如果左边层级为0, 那么返回右边层级+1 + if (left == 0) + return right + 1; + // 如果右边层级为0, 返回左边层级+1 + else if (right == 0) + return left + 1; + else //如果左右层级都不为0, 返回左右层数最小值+1 + return Math.min(left, right) + 1; + } + + + /** + * 使用层级遍历的方式: 循环+队列 一层一层遍历并记录层数, 当遇到第一个左右子节点为空的节点, 那这个节点就是最小深度的叶子节点, 返回的层数为最小层数(最小深度). + * + * 时间复杂度为O(n), 空间复杂度为O(n) + * + * @param root + * @return + */ + private static int minDepthByLoop(TreeNode root) { + int level = 0; + if (root == null) + return level; + + Queue queue = new LinkedList<>(); + queue.offer(root); + + while (!queue.isEmpty()) { + level++; + int size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + // 如果节点的左右子树都为空, 那么这个节点为叶子节点, 也就是最小深度 + if (node.left == null && node.right == null) { + return level; + } + // 将左右节点按顺序放入队列中,用于下一层遍历 + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + } + return level; + } +} From dc0076f744d8f7b79e615423526591b28c66b511 Mon Sep 17 00:00:00 2001 From: longwu Date: Tue, 14 Jan 2020 00:44:29 +0800 Subject: [PATCH 29/74] =?UTF-8?q?=E6=9C=80=E8=BF=91=E5=85=AC=E5=85=B1?= =?UTF-8?q?=E7=A5=96=E5=85=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../binarytree/LowestCommonAncestor.java | 49 +++++++++++++------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/study/binarytree/LowestCommonAncestor.java b/src/main/java/com/study/binarytree/LowestCommonAncestor.java index 09b22c7..ec4a7f2 100644 --- a/src/main/java/com/study/binarytree/LowestCommonAncestor.java +++ b/src/main/java/com/study/binarytree/LowestCommonAncestor.java @@ -37,7 +37,7 @@ public class LowestCommonAncestor { public static void main(String[] args) { - int[] arr = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; List treeNodes = TreeUtils.buildTree(arr); //TreeNode p = treeNodes.get(4); @@ -63,15 +63,15 @@ public static void main(String[] args) { /** * 递归的方式查找最近公共祖先 - * - * 先从左子树进行查找,如果找到第一个目标节点p, 则不在往下查找,而是直接从目标节点的父节点r的右子树进行查找 - * + *

+ * 先从左子树进行查找,如果找到第一个目标节点p, 那么不再往下查找,而是直接从目标节点的父节点r的右子树进行查找 + *

* 如果右子树也找到目标节点q,说明r就是p和q的最近公共祖先 - * + *

* 如果右子树没有找到目标节点q, 那么目标节点q肯定在目标节点p的子节点中. 那么p就是p和q的最近公共祖先 - * + *

* 如果左子树没有找到目标节点, 那么再从右子树找, 如果找到第一个目标节点q, 那么q就是p和q的最近公共祖先,因为p肯定是q的子节点 - * + *

* 若果左右子树都没有找到目标节点, 那么直接返回null * * @param root @@ -80,28 +80,45 @@ public static void main(String[] args) { * @return */ private static TreeNode getlca(TreeNode root, TreeNode p, TreeNode q) { - if (root == null) + if (root == null) { + System.out.println("递归往内中止返回null值"); return null; + } - if (root == p || root == q) + if (root == p || root == q) { + if (root == p) + System.out.println("递归往内中止, root == p ==" + root.val); + if (root == q) + System.out.println("递归往内中止, root == q ==" + root.val); return root; + } // 遍历左子树看有没有目标节点, 有返回目标节点, 没有返回null TreeNode left = getlca(root.left, p, q); // 如果找到目标节点, 无需下遍历, 直接从当前节点的右子树进行遍历 - + System.out.println("递归往外开始, 左边目标节点left = " + (left == null ? "null" : left.val)); // 遍历右子树有看没有目标节点, 有返回目标节点, 没有返回null TreeNode right = getlca(root.right, p, q); + System.out.println("递归往外开始, 右边目标节点right = " + (right == null ? "null" : right.val)); // 如果左右子树都有目标节点, 说明公共祖先就是它本身 - if (left != null && right != null) + if (left != null && right != null) { + System.out.println("左右子树都有目标节点,公共祖先为" + root.val); return root; - - // 如果左右子树都没有目标节点, 则返回null - if (left == null && right == null) - return null; + } // 如果目标节点都在左子树,返回左子树匹配的节点 // 如果目标节点都在右子树,返回右子树匹配的节点 - return left != null ? left : right; + //return left != null ? left : right; + + if (left != null) { + System.out.println("公共祖先在左子树, left = " + left.val); + return left; + } + if (right != null) { + System.out.println("公共祖先在右子树, right = " + left.val); + return right; + } + + return null; } } From 59385d0b0b7f14b7211ec5de71c362e07fd0a763 Mon Sep 17 00:00:00 2001 From: longwu Date: Tue, 14 Jan 2020 01:25:53 +0800 Subject: [PATCH 30/74] =?UTF-8?q?=E6=B1=82=E4=BA=8C=E5=8F=89=E6=A0=91k?= =?UTF-8?q?=E5=B1=82=E8=8A=82=E7=82=B9=E6=96=B0=E5=A2=9E=E5=BE=AA=E7=8E=AF?= =?UTF-8?q?+=E9=98=9F=E5=88=97=E7=9A=84=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/study/binarytree/NodesInALevel.java | 68 +++++++++++++++++-- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/study/binarytree/NodesInALevel.java b/src/main/java/com/study/binarytree/NodesInALevel.java index 38359fc..29a8408 100644 --- a/src/main/java/com/study/binarytree/NodesInALevel.java +++ b/src/main/java/com/study/binarytree/NodesInALevel.java @@ -3,7 +3,9 @@ import com.study.utils.TreeUtils; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; +import java.util.Queue; /** * 求第k层有多少个节点, 根节点为第0层 @@ -21,11 +23,12 @@ public static void main(String[] args) { TreeNode root = treeNodes.get(0); int k = 2; - System.out.printf("第%d层节点数:%d\r\n", k, k_nodes(root, k)); - System.out.println("分别为:"); - List nodes = k_nodes2(root, k); +// System.out.printf("第%d层节点数:%d\r\n", k, k_nodes(root, k)); +// System.out.println("分别为:"); + //List nodes = k_nodes2(root, k); + List nodes = k_nodes3(root, k); for (TreeNode node : nodes) { - System.out.println(node.val); + System.out.print(node.val + " "); } } @@ -84,24 +87,79 @@ private static List k_nodes2(TreeNode root, int k) { List list = new ArrayList(); if (k < 0) return list; - nodes(root, k, list); + //nodes(root, k, list); + nodes2(root, k, list); return list; } private static void nodes(TreeNode root, int k, List list) { if (root == null) { + System.out.println("节点为空,递归往内中止"); return; } if (k == 0) { // 将第k层节点加入到集合中 list.add(root); + System.out.println("往集合中添加节点" + root.val); } + System.out.println(String.format("递归往内, 当前节点为%d, k=%d", root.val, k)); + // 递归左(右)子树, 找出k=0节点 nodes(root.left, k - 1, list); + System.out.println(String.format("递归左子树往外, 当前节点为%d, k=%d", root.val, k)); // 递归右(左)子树, 找出k=0节点 nodes(root.right, k - 1, list); + System.out.println(String.format("递归右子树往外, 当前节点为%d, k=%d", root.val, k)); + } + + private static void nodes2(TreeNode root, int k, List list) { + if (root == null) + return; + + if (k == 0) { + list.add(root); + } + + // 无所谓左右递归顺序 + nodes2(root.right, k - 1, list); + nodes2(root.left, k - 1, list); + } + + /** + * 使用层级遍历的方式获取第k层节点数 + * + * @param root + * @param k + * @return + */ + private static List k_nodes3(TreeNode root, int k) { + List nodes = new ArrayList<>(); + + if (root == null) + return nodes; + + Queue queue = new LinkedList(); + queue.offer(root); + + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + // 获取第k层节点 + if (k == 0) { + nodes.add(node); + } + if (node.left != null) + queue.offer(node.left); + if (node.right != null) + queue.offer(node.right); + } + if (k == 0) break; + k--; + } + return nodes; } } From 5cdb2de45c5cc22e167d7b92c48ad02655eb10b2 Mon Sep 17 00:00:00 2001 From: longwu Date: Thu, 16 Jan 2020 22:46:16 +0800 Subject: [PATCH 31/74] =?UTF-8?q?=E4=B8=89=E6=95=B0=E4=B9=8B=E5=92=8C?= =?UTF-8?q?=E5=B0=8F=E4=BA=8Etarget?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/study/number/ThreeSumSmaller.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/main/java/com/study/number/ThreeSumSmaller.java diff --git a/src/main/java/com/study/number/ThreeSumSmaller.java b/src/main/java/com/study/number/ThreeSumSmaller.java new file mode 100644 index 0000000..2121fd0 --- /dev/null +++ b/src/main/java/com/study/number/ThreeSumSmaller.java @@ -0,0 +1,58 @@ +package com.study.number; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 三数之和小于target + *

+ * 题目描述:给定一个n个整数的数组和一个目标整数target,找到下标为i、j、k的数组元素0 <= i < j < k < n, + * 满足条件nums[i] + nums[j] + nums[k] < target。输出满足上述条件的组合的总数。 + */ +public class ThreeSumSmaller { + + public static void main(String[] args) { + int[] nums = {5, 1, 7, 2, 8, 9, 3, 4}; + + //List arr = threeSumSmaller(nums, 9); + List arr = threeSumSmaller(nums, 12); + + for (int i = 0; i < arr.size(); i += 3) { + System.out.println(String.format("%d %d %d", arr.get(i), arr.get(i + 1), arr.get(i + 2))); + } + } + + private static List threeSumSmaller(int[] nums, int target) { + List result = new ArrayList<>(); + if (nums == null || nums.length == 0) { + return result; + } + + // 数组排序后方可使用双指针 + Arrays.sort(nums); + + // 枚举第一个元素, 并对另外的两个元素设置左右两个指针 + for (int i = 0; i < nums.length - 2; i++) {//由于left 和 right元素在第一个元素右边,所以第一个元素只需要遍历数组长度-3个即可 + int left = i + 1; //左边指针位置 + int right = nums.length - 1; //右边指针位置 + + while (left < right) { + // 左边保持位置不变, 如果right + left 满足条件, 那么从right指针从此处一直移动到left+1的元素都满足条件, 因为数组元素是从小到大排列的,最大的right满足,那更小的right都应该满足 + if (nums[left] + nums[right] < target - nums[i]) { + // 固定left和i, 调整right, 获取所有满足条件的right + for (int j = left + 1; j <= right; j++) { + result.add(nums[i]); + result.add(nums[left]); + result.add(nums[j]); + } + left++; // 获取完 固定i和left后的right元素后, 将left往后移 + } else { + right--; + // 如果right不满足条件,比结果target 那就往前挪动一位 + } + } + } + return result; + } +} From be777ed3aea917f35cc29011f64186ef37c50471 Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 20 Jan 2020 21:35:59 +0800 Subject: [PATCH 32/74] =?UTF-8?q?=E6=B1=82=E4=BA=8C=E7=BB=B4=E7=9F=A9?= =?UTF-8?q?=E9=98=B5=E7=9A=84=E8=B5=B0=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dynamicprogramming/CountPathMatrix.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/main/java/com/study/dynamicprogramming/CountPathMatrix.java diff --git a/src/main/java/com/study/dynamicprogramming/CountPathMatrix.java b/src/main/java/com/study/dynamicprogramming/CountPathMatrix.java new file mode 100644 index 0000000..c2d40db --- /dev/null +++ b/src/main/java/com/study/dynamicprogramming/CountPathMatrix.java @@ -0,0 +1,54 @@ +package com.study.dynamicprogramming; + +/** + * 求二维矩阵的走法 + * + * 一个人从二维矩阵的起点位置[-1,-1]走到目标位置[-4,-5],只能往左或者往下,不能往回走,求一共有多少种走法. + * 思路: 如下经过从推导发现, 一共有35种走法. 其中可以发现一个规律就是每个格子的走法数量 = 右和下两个邻格走法数量之和. + * 算法推导: dp[-3,-4] = dp[-4,-4] + dp[-3,-5] + * dp[-2,-4] = dp[-3,-4] + dp[-2,-5] + * ... + * dp[-1,-1] = dp[-2,-1] + dp[-1,-2] + * dp[m,n] = dp[m-1,n] + dp[m,n-1] + */ +public class CountPathMatrix { + + public static void main(String[] args) { + + System.out.println(countPath(4,5)); + System.out.println(countPath(8,9)); + } + + /** + * 有一个宽为width,长为height的矩阵 + * 从起点[0,0] 走到终点[width-1,height-1] 一共有多少种走法 + *

+ * 解法 动态规划, 通过二维数组进行递推 + * + * 时间复杂度为O(n), 空间复杂度为O(n) + * + * @return + */ + private static int countPath(int width, int height) { + // 创建一个宽为width,长为height + int[][] matrix = new int[width][height]; + + // 先将横坐标为0 和 纵坐标为0的所有表格都填上, 走法都是一种. + // 横坐标为0的只能从左往右横着走. 纵坐标为0的只能从上往下竖着走 + for (int i = 0; i < width; i++) { + matrix[i][0] = 1; + } + for (int j = 0; j < height; j++) { + matrix[0][j] = 1; + } + + // 从横纵坐标不为0的格子开始推倒 + for (int i = 1; i < width; i++) { + for (int j = 1; j < height; j++) { + matrix[i][j] = matrix[i - 1][j] + matrix[i][j - 1]; + } + } + //返回走后一个格子的走法数量, 由于数组是从0开始,所以实际最大索引为长度-1 + return matrix[width - 1][height - 1]; + } +} From 7d043fcefb8f011b35fa9eae5a8e34c74ef6b4c7 Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 20 Jan 2020 23:52:51 +0800 Subject: [PATCH 33/74] =?UTF-8?q?=E6=B1=82=E4=BA=8C=E7=BB=B4=E7=9F=A9?= =?UTF-8?q?=E9=98=B5=E7=9A=84=E8=B5=B0=E6=B3=952(=E7=9F=B3=E5=A4=B4?= =?UTF-8?q?=E7=AF=87)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dynamicprogramming/CountPathMatrix2.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/main/java/com/study/dynamicprogramming/CountPathMatrix2.java diff --git a/src/main/java/com/study/dynamicprogramming/CountPathMatrix2.java b/src/main/java/com/study/dynamicprogramming/CountPathMatrix2.java new file mode 100644 index 0000000..4aae882 --- /dev/null +++ b/src/main/java/com/study/dynamicprogramming/CountPathMatrix2.java @@ -0,0 +1,85 @@ +package com.study.dynamicprogramming; + +import java.util.HashMap; +import java.util.Map; + +/** + * 求二维矩阵的走法2(石头篇) + *

+ * 一个人从二维矩阵的起点位置[-1,-1]走到目标位置[-4,-5],只能往左或者往下,不能往回走, 且有石头的位置不能走,求一共有多少种走法. + * 思路: 如下经过从推导发现, 一共有35种走法. 其中可以发现一个规律就是每个格子的走法数量 = 右和下两个邻格走法数量之和. + * 算法推导: + * if(dp[m][n] == "石头") + * dp[m][n] = 0 + * else + * dp[m][n] = dp[m-1][n] + dp[m][n-1] + */ +public class CountPathMatrix2 { + + public static void main(String[] args) { + + Map stones = new HashMap<>(); + // 在非边界区域放了一块石头 + stones.put(2, 3); + // 在边界区域放了一块石头 + stones.put(1, 0); + + System.out.println(countPath(4, 5, stones)); + } + + /** + * 使用动态规划 计算从[0][0] 到达[m-1][n-1]的走法有多少,这里需要排除石头所在的位置 + * + * 边界上若有石头需要重点处理 + * + * @param m 宽 + * @param n 长 + * @param stones 石头所在的位置 + * @return + */ + private static int countPath(int m, int n, Map stones) { + int[][] dp = new int[m][n]; + + // 横竖坐标为0的边界上走法都为1 + // 石头位置上的走法为0 + for (int i = 0; i < m; i++) { + dp[i][0] = 1; + } + + for (int j = 0; j < n; j++) { + dp[0][j] = 1; + } + + // 如果在边界上出现石头,那么边界这一行或者这一纵的前面的几格走法都为0 + for (Map.Entry kv : stones.entrySet()) { + // 横坐标为0的位置上有石头(竖行), 那石头以上的位置走法都为0,因为唯一往下走的位置给堵住了 + if (kv.getKey() == 0) { + int sj = kv.getValue(); //获取竖坐标 + for (; sj < n; sj++) { + dp[0][sj] = 0; //石头上面的走法全部为0 + } + } + + // 纵坐标为0的位置上有石头(横行),那石头左边的位置走法都为0,因为唯一往右走的位置给堵住了 + if (kv.getValue() == 0) { + int si = kv.getKey();// 获取横坐标 + for (; si < m; si++) { + dp[si][0] = 0; //石头左边的走法全部为0 + } + } + } + + // 计算走法 + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + // 石头位置上的走法为0 + if (stones.containsKey(i) && stones.get(i) == j) + dp[i][j] = 0; + else + dp[i][j] = dp[i][j - 1] + dp[i - 1][j]; + } + } + + return dp[m - 1][n - 1]; + } +} From 9746e64232e6dc1f6de233bd377f9ce572d7b03a Mon Sep 17 00:00:00 2001 From: longwu Date: Tue, 21 Jan 2020 22:19:44 +0800 Subject: [PATCH 34/74] =?UTF-8?q?=E6=B1=82=E4=BA=8C=E7=BB=B4=E7=9F=A9?= =?UTF-8?q?=E9=98=B5=E7=9A=84=E8=B5=B0=E6=B3=95(=E4=B8=8D=E5=90=8C?= =?UTF-8?q?=E8=B7=AF=E5=BE=84)=20=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dynamicprogramming/CountPathMatrix.java | 116 +++++++++++++++--- 1 file changed, 100 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/study/dynamicprogramming/CountPathMatrix.java b/src/main/java/com/study/dynamicprogramming/CountPathMatrix.java index c2d40db..85fef83 100644 --- a/src/main/java/com/study/dynamicprogramming/CountPathMatrix.java +++ b/src/main/java/com/study/dynamicprogramming/CountPathMatrix.java @@ -1,8 +1,10 @@ package com.study.dynamicprogramming; +import java.util.Arrays; + /** - * 求二维矩阵的走法 - * + * 求二维矩阵的走法(不同路径) + *

* 一个人从二维矩阵的起点位置[-1,-1]走到目标位置[-4,-5],只能往左或者往下,不能往回走,求一共有多少种走法. * 思路: 如下经过从推导发现, 一共有35种走法. 其中可以发现一个规律就是每个格子的走法数量 = 右和下两个邻格走法数量之和. * 算法推导: dp[-3,-4] = dp[-4,-4] + dp[-3,-5] @@ -10,45 +12,127 @@ * ... * dp[-1,-1] = dp[-2,-1] + dp[-1,-2] * dp[m,n] = dp[m-1,n] + dp[m,n-1] + *

+ * https://leetcode-cn.com/problems/unique-paths/ */ public class CountPathMatrix { public static void main(String[] args) { - System.out.println(countPath(4,5)); - System.out.println(countPath(8,9)); + //System.out.println(uniquePaths(4, 5)); + //System.out.println(uniquePaths2(4, 5)); + // System.out.println(uniquePaths2_2(4, 5)); + System.out.println(uniquePaths3(4, 5)); +// System.out.println(uniquePaths(8, 9)); +// System.out.println(uniquePaths2(8, 9)); } /** - * 有一个宽为width,长为height的矩阵 - * 从起点[0,0] 走到终点[width-1,height-1] 一共有多少种走法 + * 有一个宽为m,长为n的矩阵 + * 从起点[0,0] 走到终点[m-1,n-1] 一共有多少种走法 + *

*

* 解法 动态规划, 通过二维数组进行递推 - * - * 时间复杂度为O(n), 空间复杂度为O(n) + *

+ * 时间复杂度为O(m*n), 空间复杂度为O(m*n) * * @return */ - private static int countPath(int width, int height) { - // 创建一个宽为width,长为height - int[][] matrix = new int[width][height]; + private static int uniquePaths(int m, int n) { + // 创建一个宽为m,长为n + int[][] matrix = new int[m][n]; // 先将横坐标为0 和 纵坐标为0的所有表格都填上, 走法都是一种. // 横坐标为0的只能从左往右横着走. 纵坐标为0的只能从上往下竖着走 - for (int i = 0; i < width; i++) { + for (int i = 0; i < m; i++) { matrix[i][0] = 1; } - for (int j = 0; j < height; j++) { + for (int j = 0; j < n; j++) { matrix[0][j] = 1; } // 从横纵坐标不为0的格子开始推倒 - for (int i = 1; i < width; i++) { - for (int j = 1; j < height; j++) { + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { matrix[i][j] = matrix[i - 1][j] + matrix[i][j - 1]; } } //返回走后一个格子的走法数量, 由于数组是从0开始,所以实际最大索引为长度-1 - return matrix[width - 1][height - 1]; + return matrix[m - 1][n - 1]; + } + + /** + * 由于计算一个坐标元素的值只需要用到它的下方和右边元素的值即可. 所以我们可以使用两个一维数组来存当前这一列和相邻的右边这一列即可 + *

+ * 针对uniquePaths()方法的优化, 使用2个一维数组来节省空间, 空间复杂度变为O(2n), 时间复杂度为O(m*n) + *

+ * https://leetcode-cn.com/problems/unique-paths/solution/dong-tai-gui-hua-by-powcai-2/ + * + * @param m + * @param n + * @return + */ + private static int uniquePaths2(int m, int n) { + int[] pre = new int[n]; + int[] cur = new int[n]; + Arrays.fill(pre, 1); //将1存在数组的每个元素位置上 + Arrays.fill(cur, 1); + // 从右往左遍历每列的元素 + for (int i = 1; i < m; i++) { + // 从下往上遍历每行的元素 + for (int j = 1; j < n; j++) { + // 当前列的元素等于 相邻下方元素 + 右边元素 + cur[j] = cur[j - 1] + pre[j]; + System.out.println(String.format("j=%d", j)); + System.out.println(String.format("cur[j]=%d, cur[j-1]=%d, pre[j]=%d", cur[j], cur[j - 1], pre[j])); + } + // 一轮循环后,将当前这一列数组赋给右边这一列,便于下一轮循环使用 + pre = cur; + } + return pre[n - 1]; + } + + private static int uniquePaths2_2(int m, int n) { + int[] pre = new int[n]; + int[] cur = new int[n]; + + //将第1列和第2列用1进行填充 + Arrays.fill(pre, 1); + Arrays.fill(cur, 1); + + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + // 某一个格子的走法等于下方和右方走法之和 + cur[j] = cur[j - 1] + pre[j]; + } + pre = cur; + } + // 返回最后一列最后一行的元素 走法数 + return pre[n - 1]; + } + + /** + * 针对两列数组进行优化,将两列数组合并为一列数组,不断的使用 新值 = 上一列新值 + 旧值(右侧) 可以进一步优化为 新值 += 旧值(右侧) + * + * 由于只用了一个数组, 空间复杂度变成了O(n), 时间复杂度没变,依旧为O(m*n) + * + * @param m + * @param n + * @return + */ + private static int uniquePaths3(int m, int n) { + int[] cur = new int[n]; + Arrays.fill(cur,1); + for (int i = 1; i < m;i++){ + for (int j = 1; j < n; j++){ + // 充分利用每一列开始的时候下方格子都为1 + 上一列右边的值 = 当前值 = 1 + cur[j-1] = a + // 然后下一列 = 当前值 + 上一列右边的值 a + cur[j] = b + // 接着再下一列 = 当前值 + 上一列右边的值 b + cur[j+1] = c + //cur[j] = cur[j] + cur[j-1]; + cur[j] += cur[j-1]; + System.out.println("cur[j] = " + cur[j]); + } + } + return cur[n-1]; } } From 4f8788ffbcf03e09442f6f0eb98515c82faac86a Mon Sep 17 00:00:00 2001 From: longwu Date: Tue, 21 Jan 2020 23:31:47 +0800 Subject: [PATCH 35/74] =?UTF-8?q?=E4=B8=8D=E5=90=8C=E8=B7=AF=E5=BE=84=20II?= =?UTF-8?q?=20=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dynamicprogramming/CountPathMatrix2.java | 135 +++++++++++------- 1 file changed, 86 insertions(+), 49 deletions(-) diff --git a/src/main/java/com/study/dynamicprogramming/CountPathMatrix2.java b/src/main/java/com/study/dynamicprogramming/CountPathMatrix2.java index 4aae882..3cf740a 100644 --- a/src/main/java/com/study/dynamicprogramming/CountPathMatrix2.java +++ b/src/main/java/com/study/dynamicprogramming/CountPathMatrix2.java @@ -1,85 +1,122 @@ package com.study.dynamicprogramming; -import java.util.HashMap; -import java.util.Map; - /** * 求二维矩阵的走法2(石头篇) *

- * 一个人从二维矩阵的起点位置[-1,-1]走到目标位置[-4,-5],只能往左或者往下,不能往回走, 且有石头的位置不能走,求一共有多少种走法. - * 思路: 如下经过从推导发现, 一共有35种走法. 其中可以发现一个规律就是每个格子的走法数量 = 右和下两个邻格走法数量之和. + * 1个机器人位于1个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 + *

+ * 机器人每次只能向下或者向右移动1步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 + *

+ * 现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径? + * 网格中的障碍物和空位置分别用 1 和 0 来表示。 + *

+ * 说明:m 和 n 的值均不超过 100。 + *

+ * 示例 1: + *

+ * 输入: + * [ + *   [0,0,0], + *   [0,1,0], + *   [0,0,0] + * ] + * 输出: 2 + * 解释: + * 3x3 网格的正中间有1个障碍物。 + * 从左上角到右下角1共有 2 条不同的路径: + * 1. 向右 -> 向右 -> 向下 -> 向下 + * 2. 向下 -> 向下 -> 向右 -> 向右 + *

* 算法推导: - * if(dp[m][n] == "石头") + * if(dp[m][n] == 1) * dp[m][n] = 0 * else * dp[m][n] = dp[m-1][n] + dp[m][n-1] + *

+ * https://leetcode-cn.com/problems/unique-paths-ii/ */ public class CountPathMatrix2 { public static void main(String[] args) { - Map stones = new HashMap<>(); - // 在非边界区域放了一块石头 - stones.put(2, 3); - // 在边界区域放了一块石头 - stones.put(1, 0); + // 3行3列矩阵 1为石头 + int[][] obstacleGrid = new int[3][3]; + obstacleGrid[0] = new int[]{0, 0, 0}; + obstacleGrid[1] = new int[]{0, 1, 0}; + obstacleGrid[2] = new int[]{0, 0, 0}; + + System.out.println(uniquePathsWithObstacles(obstacleGrid)); - System.out.println(countPath(4, 5, stones)); + + // 5行4列矩阵 1为石头 + obstacleGrid = new int[5][4]; + obstacleGrid[0] = new int[]{0, 0, 0, 0}; + obstacleGrid[1] = new int[]{0, 1, 0, 0}; + obstacleGrid[2] = new int[]{0, 0, 0, 0}; + obstacleGrid[3] = new int[]{0, 0, 0, 0}; + obstacleGrid[4] = new int[]{0, 0, 1, 0}; + + System.out.println(uniquePathsWithObstacles(obstacleGrid)); } /** - * 使用动态规划 计算从[0][0] 到达[m-1][n-1]的走法有多少,这里需要排除石头所在的位置 - * - * 边界上若有石头需要重点处理 - * - * @param m 宽 - * @param n 长 - * @param stones 石头所在的位置 * @return */ - private static int countPath(int m, int n, Map stones) { - int[][] dp = new int[m][n]; + public static int uniquePathsWithObstacles(int[][] obstacleGrid) { - // 横竖坐标为0的边界上走法都为1 - // 石头位置上的走法为0 - for (int i = 0; i < m; i++) { - dp[i][0] = 1; - } + int r = obstacleGrid.length; + int c = obstacleGrid[0].length; - for (int j = 0; j < n; j++) { - dp[0][j] = 1; - } + // 终点堵住了 走法为0 + if (obstacleGrid[0][0] == 1) + return 0; - // 如果在边界上出现石头,那么边界这一行或者这一纵的前面的几格走法都为0 - for (Map.Entry kv : stones.entrySet()) { - // 横坐标为0的位置上有石头(竖行), 那石头以上的位置走法都为0,因为唯一往下走的位置给堵住了 - if (kv.getKey() == 0) { - int sj = kv.getValue(); //获取竖坐标 - for (; sj < n; sj++) { - dp[0][sj] = 0; //石头上面的走法全部为0 + // 对网格进行填充, 第1行和第1列都填为1 + // 对第1行进行填充 + for (int i = 0; i < r; i++) { + // 如果第1行上有石头, 需要将该行后面的元素全部改为0,因为石头阻挡了道路,走法都为0 + if (obstacleGrid[i][0] == 1) { + for (; i < r; i++) { + obstacleGrid[i][0] = 0; } + } else { + // 没有石头影响的地方 走法为1 + obstacleGrid[i][0] = 1; } + } - // 纵坐标为0的位置上有石头(横行),那石头左边的位置走法都为0,因为唯一往右走的位置给堵住了 - if (kv.getValue() == 0) { - int si = kv.getKey();// 获取横坐标 - for (; si < m; si++) { - dp[si][0] = 0; //石头左边的走法全部为0 + // 对第1列进行填充, 从第1列的第2行开始, 因为第1列第1行和第1行重复了 + for (int j = 1; j < c; j++) { + // 如果第1列上有石头, 需要对该列以上的元素全部该为0,因为石头阻挡了道路,走法为0 + if (obstacleGrid[0][j] == 1) { + for (; j < c; j++) { + obstacleGrid[0][j] = 0; } + } else { + // 没有石头影响的地方 走法为1 + obstacleGrid[0][j] = 1; } } - // 计算走法 - for (int i = 1; i < m; i++) { - for (int j = 1; j < n; j++) { + // 计算走法, 从第2行第2列开始 + for (int i = 1; i < r; i++) { + for (int j = 1; j < c; j++) { // 石头位置上的走法为0 - if (stones.containsKey(i) && stones.get(i) == j) - dp[i][j] = 0; - else - dp[i][j] = dp[i][j - 1] + dp[i - 1][j]; + if (obstacleGrid[i][j] == 1) + obstacleGrid[i][j] = 0; + else// 没石头的位置走法 = 相邻下面 + 相邻右边 + obstacleGrid[i][j] = obstacleGrid[i][j - 1] + obstacleGrid[i - 1][j]; } } - return dp[m - 1][n - 1]; + return obstacleGrid[r - 1][c - 1]; } + + /** + * @param obstacleGrid + * @return + */ +// private static int countPath2(int[][] obstacleGrid) { +// +// } } From 47578f0cd96dd7ceba78b798867155ff37d860a8 Mon Sep 17 00:00:00 2001 From: longwu Date: Wed, 22 Jan 2020 16:46:39 +0800 Subject: [PATCH 36/74] =?UTF-8?q?=E4=B8=89=E8=A7=92=E5=BD=A2=E6=9C=80?= =?UTF-8?q?=E5=B0=8F=E8=B7=AF=E5=BE=84=E5=92=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/dynamicprogramming/Triangle.java | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/main/java/com/study/dynamicprogramming/Triangle.java diff --git a/src/main/java/com/study/dynamicprogramming/Triangle.java b/src/main/java/com/study/dynamicprogramming/Triangle.java new file mode 100644 index 0000000..043c9ca --- /dev/null +++ b/src/main/java/com/study/dynamicprogramming/Triangle.java @@ -0,0 +1,94 @@ +package com.study.dynamicprogramming; + +import java.util.*; + +/** + * 三角形最小路径和 + *

+ * 给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。 + *

+ * 例如,给定三角形: + *

+ * [ + * [2], + * [3,4], + * [6,5,7], + * [4,1,8,3] + * ] + * 自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。 + *

+ * 说明: + *

+ * 如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。 + *

+ * 链接:https://leetcode-cn.com/problems/triangle + */ +public class Triangle { + + /** + * 这个题解法跟贪心不一样的是, 它要求全局最优值, 而不是当前最优值 + *

+ * 每个点[i][j] 只能访问下一行相邻的两个点 也就是[i+1][j] 和 [i+1][j+1] + * + * @param args + */ + public static void main(String[] args) { + List> triangle = new ArrayList<>(); + triangle.add(Arrays.asList(2)); + triangle.add(Arrays.asList(3, 4)); + triangle.add(Arrays.asList(6, 5, 7)); + triangle.add(Arrays.asList(4, 1, 8, 3)); + + //System.out.println(minimumTotal(triangle)); + System.out.println(minimumTotal2(triangle)); + } + + /** + * 动态规划法 + *

+ * 从后往前推, 修改每个点的值为其最小路径和, 一层层往上,最终顶点的最短路径 = 顶点值 + 第二层最短路径的那个点的值 + *

+ * 时间复杂度为O(m*n) + *

+ * https://leetcode-cn.com/problems/triangle/solution/120-san-jiao-xing-zui-xiao-lu-jing-he-by-alexer-66/ + * + * @param triangle + * @return + */ + public static int minimumTotal(List> triangle) { + + if (triangle.size() == 0) + return 0; + + // 从后面往前推, i为倒数第二行, 下面的i+1则为倒数第一行 + // 每一层推完后得出当前层每个节点的最小路径和, 最终将顶点值+第二层最小路径和的那个点 = 最短路径和 + for (int i = triangle.size() - 2; i >= 0; i--) { + for (int j = 0; j < triangle.get(i).size(); j++) { + // 当前节点的最短路径path = 当前节点下一层两个节点中最短那个节点的路径值 + 当前节点的路径值 + int path = Math.min(triangle.get(i + 1).get(j), triangle.get(i + 1).get(j + 1)) + triangle.get(i).get(j); + triangle.get(i).set(j, path);//修改当前节点路径值为最短路径值 + } + } + + // 最终结果 + return triangle.get(0).get(0); + } + + + public static int minimumTotal2(List> triangle) { + if (triangle.size() == 0) { + return 0; + } + + // 遍历从倒数第2行开始 直到 最顶层元素 + for (int i = triangle.size() - 2; i >= 0; i--) { + for (int j = 0; j < triangle.get(i).size(); j++) { + int minPath = Math.min(triangle.get(i+1).get(j), triangle.get(i+1).get(j+1)); + int pathSum = triangle.get(i).get(j) + minPath; + triangle.get(i).set(j, pathSum); + } + } + + return triangle.get(0).get(0); + } +} From e7bf14be733e68c79743e4e06ad60bc9364aca60 Mon Sep 17 00:00:00 2001 From: longwu Date: Wed, 22 Jan 2020 16:58:30 +0800 Subject: [PATCH 37/74] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/dynamicprogramming/Triangle.java | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/study/dynamicprogramming/Triangle.java b/src/main/java/com/study/dynamicprogramming/Triangle.java index 043c9ca..a09036f 100644 --- a/src/main/java/com/study/dynamicprogramming/Triangle.java +++ b/src/main/java/com/study/dynamicprogramming/Triangle.java @@ -39,14 +39,25 @@ public static void main(String[] args) { triangle.add(Arrays.asList(6, 5, 7)); triangle.add(Arrays.asList(4, 1, 8, 3)); - //System.out.println(minimumTotal(triangle)); - System.out.println(minimumTotal2(triangle)); + System.out.println(minimumTotal(triangle)); + //System.out.println(minimumTotal2(triangle)); } /** * 动态规划法 *

* 从后往前推, 修改每个点的值为其最小路径和, 一层层往上,最终顶点的最短路径 = 顶点值 + 第二层最短路径的那个点的值 + * + * 2 + * 3 4 + * 6 5 7 + * 4 1 8 3 + * 最终三角形被改成了 + * 11 + * 9 10 + * 7 6 10 + * 4 1 8 3 + * *

* 时间复杂度为O(m*n) *

@@ -57,6 +68,8 @@ public static void main(String[] args) { */ public static int minimumTotal(List> triangle) { + print(triangle); + if (triangle.size() == 0) return 0; @@ -71,11 +84,15 @@ public static int minimumTotal(List> triangle) { } // 最终结果 + print(triangle); return triangle.get(0).get(0); } public static int minimumTotal2(List> triangle) { + + print(triangle); + if (triangle.size() == 0) { return 0; } @@ -83,12 +100,21 @@ public static int minimumTotal2(List> triangle) { // 遍历从倒数第2行开始 直到 最顶层元素 for (int i = triangle.size() - 2; i >= 0; i--) { for (int j = 0; j < triangle.get(i).size(); j++) { - int minPath = Math.min(triangle.get(i+1).get(j), triangle.get(i+1).get(j+1)); + int minPath = Math.min(triangle.get(i + 1).get(j), triangle.get(i + 1).get(j + 1)); int pathSum = triangle.get(i).get(j) + minPath; triangle.get(i).set(j, pathSum); } } - + print(triangle); return triangle.get(0).get(0); } + + private static void print(List> triangle) { + for (int i = 0; i < triangle.size(); i++) { + for (int j = 0; j < triangle.get(i).size(); j++) { + System.out.print(triangle.get(i).get(j) + " "); + } + System.out.println(); + } + } } From e579b4e30cacbdc8af4bba15eb0e0f0cf17c25c5 Mon Sep 17 00:00:00 2001 From: longwu Date: Wed, 22 Jan 2020 21:50:12 +0800 Subject: [PATCH 38/74] =?UTF-8?q?=E4=B9=98=E7=A7=AF=E6=9C=80=E5=A4=A7?= =?UTF-8?q?=E5=AD=90=E5=BA=8F=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MaximumProductSubarray.java | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/main/java/com/study/dynamicprogramming/MaximumProductSubarray.java diff --git a/src/main/java/com/study/dynamicprogramming/MaximumProductSubarray.java b/src/main/java/com/study/dynamicprogramming/MaximumProductSubarray.java new file mode 100644 index 0000000..7c2e666 --- /dev/null +++ b/src/main/java/com/study/dynamicprogramming/MaximumProductSubarray.java @@ -0,0 +1,95 @@ +package com.study.dynamicprogramming; + +/** + * 乘积最大子序列 + *

+ * 给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。 + *

+ * 示例 1: + *

+ * 输入: [2,3,-2,4] + * 输出: 6 + * 解释: 子数组 [2,3] 有最大乘积 6。 + * 示例 2: + *

+ * 输入: [-2,0,-1] + * 输出: 0 + * 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。 + *

+ * 链接:https://leetcode-cn.com/problems/maximum-product-subarray + */ +public class MaximumProductSubarray { + + public static void main(String[] args) { + int[] nums = {2, 3, -2, 4}; + //int[] nums = {-2, 0, -1}; + //int[] nums = {0, 2}; + System.out.println(maxProduct(nums)); + System.out.println(maxProduct2(nums)); + } + + /** + * 使用穷举法(暴力法), 获取所有元素的值 以及 所有连续元素的乘积, 求其中的最大值 + *

+ * 时间复杂度O(m*n) 空间复杂度O(1) + * + * @param nums + * @return + */ + private static int maxProduct(int[] nums) { + if (nums.length == 0) + return 0; + + int max = nums[0]; + for (int i = 0; i < nums.length; i++) { + // 最大乘积可以是单个元素 + int result = nums[i]; +// if (result > max) +// max = result; + max = Math.max(result, max); + + // 最大乘积可以是多个元素乘积 + // 每个元素为了确保不重复,只能乘以自己后面的元素 + // 乘以自己前面的元素结果跟前面的元素乘以自己是一样的,所以重复了 + int j = i + 1; + while (j < nums.length) { + result *= nums[j]; +// if (result > max) +// max = result; + max = Math.max(result, max); + j++; + } + } + return max; + } + + /** + * 标签:动态规划 + * 遍历数组时计算当前最大值,不断更新 + * 令imax为当前最大值,则当前最大值为 imax = max(imax * nums[i], nums[i]) + * 由于存在负数,那么会导致最大的变最小的,最小的变最大的。因此还需要维护当前最小值imin,imin = min(imin * nums[i], nums[i]) + * 当负数出现时则imax与imin进行交换再进行下一步计算 + * 时间复杂度:O(n) + *

+ * 作者:guanpengchn + * 链接:https://leetcode-cn.com/problems/maximum-product-subarray/solution/hua-jie-suan-fa-152-cheng-ji-zui-da-zi-xu-lie-by-g/ + * + * @param nums + * @return + */ + private static int maxProduct2(int[] nums) { + int max = Integer.MIN_VALUE, imax = 1, imin = 1; + for (int i = 0; i < nums.length; i++) { + if (nums[i] < 0) { + int tmp = imax; + imax = imin; + imin = tmp; + } + imax = Math.max(imax * nums[i], nums[i]); + imin = Math.min(imin * nums[i], nums[i]); + + max = Math.max(max, imax); + } + return max; + } +} From 2c46bccd6d4c15ea848be09a38dbe9f9f6631fe8 Mon Sep 17 00:00:00 2001 From: longwu Date: Thu, 23 Jan 2020 00:10:26 +0800 Subject: [PATCH 39/74] =?UTF-8?q?=E6=8B=AC=E5=8F=B7=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/study/string/GenerateParentheses.java | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/main/java/com/study/string/GenerateParentheses.java diff --git a/src/main/java/com/study/string/GenerateParentheses.java b/src/main/java/com/study/string/GenerateParentheses.java new file mode 100644 index 0000000..ffad638 --- /dev/null +++ b/src/main/java/com/study/string/GenerateParentheses.java @@ -0,0 +1,134 @@ +package com.study.string; + +import java.util.ArrayList; +import java.util.List; + +/** + * 括号生成 + *

+ * 给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。 + *

+ * 例如,给出 n = 3,生成结果为: + *

+ * [ + * "((()))", + * "(()())", + * "(())()", + * "()(())", + * "()()()" + * ] + *

+ * 链接:https://leetcode-cn.com/problems/generate-parentheses + */ +public class GenerateParentheses { + + /** + * 有几个需要注意的条件: + * 1. 左括号和右括号数量必须一致 + * 2. 必须先有左括号 + * + * @param args + */ + public static void main(String[] args) { + List resultList = generateParenthesis(3); + + for (String result : resultList) { + System.out.println(result); + } + } + + private static List generateParenthesis(int n) { + List resultList = new ArrayList<>(); + //gen(0, 0, n, "", resultList); + //gen2(0, 0, n, "", resultList); + gen3(0, 0, n, "", resultList); + + return resultList; + } + + + /** + * 使用递归枚举所有左右组合的情况, 增加一些限定条件(必须先有左括号再有右括号, 而且过程中, 右括号数量不能大于左括号) + *

+ * 时间复杂度O + * + * @param left + * @param right + * @param n + * @param result + * @param resultList + */ + private static void gen(int left, int right, int n, String result, List resultList) { + // 当左右括号都满足n个的时候, 就达到了结果要求 + if (left == n && right == n) { + resultList.add(result); + System.out.println("递归从外往内终止: result = " + result); + return; + } + + // 左括号增加条件: 左括号数量不到n个 + if (left < n) { + System.out.println("左括号递归从外往内开始: result = " + result); + gen(left + 1, right, n, result + "(", resultList); + } + + System.out.println("左括号递归从内往外开始: result = " + result); + + // 右括号增加条件: 数量少于左括号, 并且不满足n个(这里因为left小于n, 所以right必然小于n) + if (left > right) { + System.out.println("右括号递归从外往内开始: result = " + result); + gen(left, right + 1, n, result + ")", resultList); + } + + System.out.println("右括号递归从内往外开始: result = " + result); + } + + + private static void gen2(int left, int right, int n, String result, List resultList) { + // 结束递归的条件 + if (left == n && right == n) { + resultList.add(result); + return; + } + + // 先有左括号 再有右括号, 而且右括号一定不能大于左括号数量 + if (left < n) { + gen2(left + 1, right, n, result + "(", resultList); + } + + if (right < left) { + gen2(left, right + 1, n, result + ")", resultList); + } + } + + /** + * 递归中使用剪枝 或者也叫回溯 + * + * @param left + * @param right + * @param n + * @param result + * @param resultList + */ + private static void gen3(int left, int right, int n, String result, List resultList) { + // 结束递归的条件 + if (left == n && right == n) { + resultList.add(result); + return; + } + + // 使用剪枝的方式 去除右括号大于左括号的所有情况 + // 剪枝 也是回溯法的一种解题思路, 因为当前做法不满足条件,所以直接返回避免无效操作 + if(left < right) + return; + + // 先有左括号 再有右括号, 而且右括号一定不能大于左括号数量 + if (left < n) { + gen2(left + 1, right, n, result + "(", resultList); + } + + if (right < n) { + gen2(left, right + 1, n, result + ")", resultList); + } + } +} From f69057a3b59a2a6de82734d95939bbd6f8622926 Mon Sep 17 00:00:00 2001 From: longwu Date: Thu, 23 Jan 2020 18:40:08 +0800 Subject: [PATCH 40/74] =?UTF-8?q?=E6=B1=82=E5=B9=B3=E6=96=B9=E6=A0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/study/number/Sqrt.java | 151 +++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 src/main/java/com/study/number/Sqrt.java diff --git a/src/main/java/com/study/number/Sqrt.java b/src/main/java/com/study/number/Sqrt.java new file mode 100644 index 0000000..57d7c14 --- /dev/null +++ b/src/main/java/com/study/number/Sqrt.java @@ -0,0 +1,151 @@ +package com.study.number; + +/** + * x 的平方根 + *

+ * 实现 int sqrt(int x) 函数。 + *

+ * 计算并返回 x 的平方根,其中 x 是非负整数。 + *

+ * 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。 + *

+ * 示例 1: + *

+ * 输入: 4 + * 输出: 2 + * 示例 2: + *

+ * 输入: 8 + * 输出: 2 + * 说明: 8 的平方根是 2.82842..., + *   由于返回类型是整数,小数部分将被舍去。 + *

+ * 链接:https://leetcode-cn.com/problems/sqrtx + */ +public class Sqrt { + + public static void main(String[] args) { + //int a = (0 + 2 + 1) / 2; + //int a = (0 + 2 + 1) >>> 1; + //System.out.println(a); + + + int x = 1; + //int x = 3; + //int x = 4; + //int x = 8; + //int x = 2; + //int x = 2147395599; + //System.out.println(String.format("%d的平方根为: %d", x, (int)Math.sqrt(x))); + //System.out.println(String.format("%d的平方根为: %d", x, mySqrt(x))); + //System.out.println(String.format("%d的平方根为: %d", x, mySqrt2(x))); + //System.out.println(String.format("%d的平方根为: %d", x, mySqrtForcely(x))); + + System.out.println(String.format("%d的平方根为: %d", x, mySqrt_2(x))); + x = 2; + System.out.println(String.format("%d的平方根为: %d", x, mySqrt_2(x))); + x = 3; + System.out.println(String.format("%d的平方根为: %d", x, mySqrt_2(x))); + x = 4; + System.out.println(String.format("%d的平方根为: %d", x, mySqrt_2(x))); + x = 5; + System.out.println(String.format("%d的平方根为: %d", x, mySqrt_2(x))); + x = 9; + System.out.println(String.format("%d的平方根为: %d", x, mySqrt_2(x))); + } + + /** + * 使用暴力法一个个尝试, 范围为从 x/2 +1 到 0 + * 时间复杂度为O(N) + * 不推荐, 容易超时 + * + * @param x + * @return + */ + private static int mySqrtForcely(int x) { + // 考虑到result * result 会超过int 最大值范围, 这里需要使用long类型来存储 + // 平方根一定小于平方值的一半+1 + long result = x / 2 + 1; // +1是为了处理x为1的特殊情况 + + while (result > 0) { + long square = result * result; + + // 如果平方和大于x, 就每次减1, 一个个尝试直到成功 + if (square > x) { + result--; + } else { + break; + } + } + return (int) result; + } + + /** + * 通过二分查找提高快速定位值 + *

+ * 时间复杂度为O(logN) + * + * @param x + * @return + */ + private static int mySqrt(int x) { + // 为了照顾到0,将左边界值设为0 + long left = 0; + // 为了照顾到1,将有右界值设为1 + long right = x / 2 + 1; + + while (left < right) { + // 这里中位数 左边范围值+右边范围值+1, 往右取中位数, 往左取中位数可能会因为少取 导致获取不到结果 + long mid = (left + right + 1) / 2; + long square = mid * mid; + // 如果中位数的平方大于该数, 就将最大范围缩小,改为中间值-1 + if (square > x) + right = mid - 1; + else // 由于x的平方根很难刚好为整数,所以有时候取不到相等的值, 所以只能是无限接近左边 + // 如果中位数的平方小于该数, 就将最小范围调大,改为中位数 + left = mid; //给结果赋值中位数是因为结果可能是小数, 它大于中位数,但是小于中位数+1. 而且该中位数折半之前也是+1的,比实际中位数大一点 + } + // 返回一个相等或者最接近的值 + return (int) left; + } + + + private static int mySqrt_2(int x){ + long left = 0;// 处理左边界x=0的情况 + long right = x/2 +1; //处理右边界x=1的情况 + + while(left < right){ + long mid = (left + right + 1) /2; //往右取中位数 + long square = mid * mid; + if(square > x){ + right = mid -1; + }else + left = mid; + } + return (int)left; + } + + + private static int mySqrt3(int x) { + // 处理1的特殊情况 + if (x == 1) + return x; + + long left = 0; + long right = x / 2; //这里无法处理x=1的情况 + + while (left < right) { + // 这里中位数 左边范围值+右边范围值+1, 往右取中位数, 往左取中位数可能会因为少取 导致获取不到结果(出现死循环) + long mid = (left + right +1) / 2; + long square = mid * mid; + // 如果中位数的平方大于该数, 就将最大范围缩小,改为中间值-1 + if (square > x) + right = mid - 1; + else // 由于x的平方根很难刚好为整数,所以有时候取不到相等的值, 所以只能是无限接近左边 + // 如果中位数的平方小于该数, 就将最小范围调大,改为中位数 + left = mid; //给结果赋值中位数是因为结果可能是小数, 它大于中位数,但是小于中位数+1. 而且该中位数折半之前也是+1的,比实际中位数大一点 + } + // 返回一个相等或者最接近的值 + return (int) left; + } +} From 93f871b8f3eac8fcca47a0e9dc68739cfc32c8e9 Mon Sep 17 00:00:00 2001 From: longwu Date: Thu, 23 Jan 2020 21:37:01 +0800 Subject: [PATCH 41/74] =?UTF-8?q?=E5=B9=B3=E6=96=B9=E6=A0=B9=E4=BC=98?= =?UTF-8?q?=E5=8C=96,=E5=A2=9E=E5=8A=A0=E7=89=9B=E9=A1=BF=E8=BF=AD?= =?UTF-8?q?=E4=BB=A3=E6=B3=95=E5=92=8C=E6=9B=B4=E5=A5=BD=E7=90=86=E8=A7=A3?= =?UTF-8?q?=E7=9A=84=E4=BA=8C=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/study/number/Sqrt.java | 79 ++++++++++++++++++------ 1 file changed, 60 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/study/number/Sqrt.java b/src/main/java/com/study/number/Sqrt.java index 57d7c14..33aec7e 100644 --- a/src/main/java/com/study/number/Sqrt.java +++ b/src/main/java/com/study/number/Sqrt.java @@ -41,17 +41,17 @@ public static void main(String[] args) { //System.out.println(String.format("%d的平方根为: %d", x, mySqrt2(x))); //System.out.println(String.format("%d的平方根为: %d", x, mySqrtForcely(x))); - System.out.println(String.format("%d的平方根为: %d", x, mySqrt_2(x))); + System.out.println(String.format("%d的平方根为: %d", x, mySqrt4(x))); x = 2; - System.out.println(String.format("%d的平方根为: %d", x, mySqrt_2(x))); - x = 3; - System.out.println(String.format("%d的平方根为: %d", x, mySqrt_2(x))); - x = 4; - System.out.println(String.format("%d的平方根为: %d", x, mySqrt_2(x))); - x = 5; - System.out.println(String.format("%d的平方根为: %d", x, mySqrt_2(x))); - x = 9; - System.out.println(String.format("%d的平方根为: %d", x, mySqrt_2(x))); + System.out.println(String.format("%d的平方根为: %d", x, mySqrt4(x))); +// x = 3; +// System.out.println(String.format("%d的平方根为: %d", x, mySqrt4(x))); +// x = 4; +// System.out.println(String.format("%d的平方根为: %d", x, mySqrt4(x))); +// x = 5; +// System.out.println(String.format("%d的平方根为: %d", x, mySqrt4(x))); +// x = 9; +// System.out.println(String.format("%d的平方根为: %d", x, mySqrt4(x))); } /** @@ -110,19 +110,19 @@ private static int mySqrt(int x) { } - private static int mySqrt_2(int x){ + private static int mySqrt_2(int x) { long left = 0;// 处理左边界x=0的情况 - long right = x/2 +1; //处理右边界x=1的情况 + long right = x / 2 + 1; //处理右边界x=1的情况 - while(left < right){ - long mid = (left + right + 1) /2; //往右取中位数 + while (left < right) { + long mid = (left + right + 1) / 2; //往右取中位数 long square = mid * mid; - if(square > x){ - right = mid -1; - }else + if (square > x) { + right = mid - 1; + } else left = mid; } - return (int)left; + return (int) left; } @@ -136,7 +136,7 @@ private static int mySqrt3(int x) { while (left < right) { // 这里中位数 左边范围值+右边范围值+1, 往右取中位数, 往左取中位数可能会因为少取 导致获取不到结果(出现死循环) - long mid = (left + right +1) / 2; + long mid = (left + right + 1) / 2; long square = mid * mid; // 如果中位数的平方大于该数, 就将最大范围缩小,改为中间值-1 if (square > x) @@ -148,4 +148,45 @@ private static int mySqrt3(int x) { // 返回一个相等或者最接近的值 return (int) left; } + + private static int mySqrt4(int x) { + if (x == 0 || x == 1) + return x; + + long left = 1; + long right = x; + long result = 0; + + while (left <= right) { + long mid = (left + right) / 2; + long square = mid * mid; + if (square == x) { + return (int) mid; + } + // 大于x, 缩小右界, 从左半部分查找 + if (square > x) { + right = mid - 1; + } else { + // 否则增大左界, 从右半部分查找 + left = mid + 1; + result = mid; + } + } + return (int) result; + } + + /** + * 牛顿迭代法 + * 时间复杂度O(logN) + * + * @param x + * @return + */ + private static int mySqrt5(int x) { + long r = x; + while (r * r > x) { + r = (r + x / r) / 2; + } + return (int)r; + } } From 809c07844efbe4d2242e840df447c4077f13c819 Mon Sep 17 00:00:00 2001 From: longwu Date: Thu, 23 Jan 2020 22:42:32 +0800 Subject: [PATCH 42/74] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/array/ArrayOperation.java | 8 +- .../NRepeatedElementInSizeTwoNArray.java | 10 ++- .../java/com/study/cache/LRUCacheClient.java | 2 +- .../ClimbingStairs2Steps.java | 12 ++- .../ClimbingStairs3Steps.java | 6 +- .../dynamicprogramming/FibonacciSequence.java | 41 ++++++++- .../java/com/study/linkedlist/Reverse.java | 41 ++++++--- .../java/com/study/number/FactorialN.java | 11 ++- .../java/com/study/number/KthLargest.java | 40 ++++++--- .../com/study/number/MajorityElement.java | 14 ++++ .../java/com/study/number/MaxOneAndTwo.java | 25 +++++- src/main/java/com/study/number/PowXN.java | 62 ++++++++++++-- src/main/java/com/study/number/Prime.java | 47 ++++++++--- .../SameNumberPossition.java | 7 +- .../number/SumofArithmeticSequences.java | 9 +- src/main/java/com/study/number/ThreeSum.java | 43 ++++++++-- src/main/java/com/study/search/BFS.java | 3 + .../java/com/study/search/BinarySearch.java | 34 +++++++- src/main/java/com/study/sort/QuickSort.java | 84 +++++++++++-------- .../com/study/string/GenerateParentheses.java | 33 +++++++- .../com/study/string/LongestPalindrome.java | 43 +++++++++- .../java/com/study/string/ValidAnagram.java | 42 ++++++++-- src/main/java/com/study/trietree/Trie.java | 26 +++--- .../java/com/study/trietree/TrieNode.java | 7 +- 24 files changed, 515 insertions(+), 135 deletions(-) rename src/main/java/com/study/{array => number}/SameNumberPossition.java (67%) diff --git a/src/main/java/com/study/array/ArrayOperation.java b/src/main/java/com/study/array/ArrayOperation.java index 9fa9c3d..fb7efe1 100644 --- a/src/main/java/com/study/array/ArrayOperation.java +++ b/src/main/java/com/study/array/ArrayOperation.java @@ -19,12 +19,12 @@ public static void main(String[] args) { } - private static int[] work (int[] a ) { + private static int[] work(int[] a) { int p = 0, n = a.length; int[] array = new int[n]; - for (int i=0; i0 && array[p-1]%2!=0) + for (int i = 0; i < n; i++) { + if (a[i] % 2 == 0) //如果是偶数 + while (p > 0 && array[p - 1] % 2 != 0) array[p--] = 0; //删除前面的奇数 array[p++] = a[i]; } diff --git a/src/main/java/com/study/array/NRepeatedElementInSizeTwoNArray.java b/src/main/java/com/study/array/NRepeatedElementInSizeTwoNArray.java index 4f306db..feb3993 100644 --- a/src/main/java/com/study/array/NRepeatedElementInSizeTwoNArray.java +++ b/src/main/java/com/study/array/NRepeatedElementInSizeTwoNArray.java @@ -38,8 +38,8 @@ public class NRepeatedElementInSizeTwoNArray { public static void main(String[] args) { //int[] a = {1, 2, 3, 3}; - //int[] a = {2, 1, 2, 5, 3, 2}; - int[] a = {5, 1, 5, 2, 5, 3, 5, 4}; + int[] a = {2, 1, 2, 5, 3, 2}; + //int[] a = {5, 1, 5, 2, 5, 3, 5, 4}; //int[] a = {9, 5, 3, 3}; //int[] a = {2, 1, 1, 1, 2, 5, 3, 2}; //int[] a = {2, 2, 4, 4, 2, 5, 3, 2}; @@ -58,16 +58,22 @@ private static int repeatedNTimes(int[] A) { HashMap map = new HashMap(A.length); for (int i = 0; i < A.length; i++) { + // 如果map中的key已经包含了这个元素 if (map.containsKey(A[i])) { + // 如果这个元素的个数已经达到数组的一半 直接返回 if (map.get(A[i]) == A.length / 2) { return A[i]; } else { + // 否则元素个数+1 map.put(A[i], map.get(A[i]) + 1); } } else { + // 该元素不存在map中, 将元素作为key 放入map中 map.put(A[i], 1); } } + + // 如果上面没有返回个数等于数组一半的元素, 说明这个元素是数组的最后一个 for (Map.Entry entry : map.entrySet()) { if (entry.getValue() == A.length / 2) { return entry.getKey(); diff --git a/src/main/java/com/study/cache/LRUCacheClient.java b/src/main/java/com/study/cache/LRUCacheClient.java index e2f7a67..0652f94 100644 --- a/src/main/java/com/study/cache/LRUCacheClient.java +++ b/src/main/java/com/study/cache/LRUCacheClient.java @@ -36,7 +36,7 @@ public static void main(String[] args) { // 元素的热程度为最新放的最热, 下面是2最热 cache.put(1, 1); cache.put(2, 2); - // 获取key为1的值, 获取后 key为1的节点由于会到链表的尾部,变成最热的节点(不被淘汰的节点) + // 获取key为1的值, 获取后 key为1的节点由于会到链表的头部,变成最热的节点(最不容易不被淘汰的节点) System.out.println("cache.get(1): " + cache.get(1)); // 返回 1 // 之后再将一个key为3的元素放入缓存中, 放完之后3的热度比1高 // 由于缓存只能放2个元素,所以 diff --git a/src/main/java/com/study/dynamicprogramming/ClimbingStairs2Steps.java b/src/main/java/com/study/dynamicprogramming/ClimbingStairs2Steps.java index 6da67f6..efd22d8 100644 --- a/src/main/java/com/study/dynamicprogramming/ClimbingStairs2Steps.java +++ b/src/main/java/com/study/dynamicprogramming/ClimbingStairs2Steps.java @@ -5,7 +5,12 @@ *

* 问题: 有n层台阶, 每次可以走1层或2层 求有多少种走法 *

- * 和斐波拉契数列是相同的解法 f(n) = f(n-1) + f(n-2) + * + * 每层楼梯的走法为前两层走法之和 + * + * 和斐波拉契数列是相同的解法 + * f(n) = f(n-1) + f(n-2) + * f(0) = f(1) = 1 */ public class ClimbingStairs2Steps { @@ -34,7 +39,7 @@ private static int calcStepsByRecursion(int n) { } /** - * 使用迭代的方式, 时间复杂都为O(n), 但依旧创建了大量重复变量 + * 使用迭代的方式, 时间复杂都为O(n), 只用了3个变量 空间复杂度为O(1) * @param n * @return */ @@ -58,7 +63,7 @@ private static int calcStepsByLoop(int n) { /** * 使用动态规划的方式, 数组来存储每个元素,节省了大量临时变量的创建 * - * 时间复杂度为O(n) + * 时间复杂度为O(n),空间复杂度为O(n) * * @param n * @return @@ -80,5 +85,4 @@ private static int calcStepsByArray(int n) { // 返回第n个元素, 数组是从0开始的 return arr[n - 1]; } - } diff --git a/src/main/java/com/study/dynamicprogramming/ClimbingStairs3Steps.java b/src/main/java/com/study/dynamicprogramming/ClimbingStairs3Steps.java index 1d1903b..5dd9a6a 100644 --- a/src/main/java/com/study/dynamicprogramming/ClimbingStairs3Steps.java +++ b/src/main/java/com/study/dynamicprogramming/ClimbingStairs3Steps.java @@ -34,9 +34,9 @@ public static void main(String[] args) { } /** - * 使用迭代的解法 + * 使用迭代的解法, 只用到了3个变量 *

- * 时间复杂度为O(n) + * 时间复杂度为O(n),空间复杂度为O(1) * * @param n * @return @@ -99,6 +99,8 @@ private static int climbStairsByArray(int n) { /** * 使用递归的解法 * + * 时间复杂度 O(3^n) + * * @param n * @return */ diff --git a/src/main/java/com/study/dynamicprogramming/FibonacciSequence.java b/src/main/java/com/study/dynamicprogramming/FibonacciSequence.java index c352373..b3c3ed5 100644 --- a/src/main/java/com/study/dynamicprogramming/FibonacciSequence.java +++ b/src/main/java/com/study/dynamicprogramming/FibonacciSequence.java @@ -15,18 +15,25 @@ public class FibonacciSequence { public static void main(String[] args) { int n = 1; + int[] cache = new int[n]; //System.out.println(String.format("第%d个数为: %d", n, fibByRecursion(n)); + System.out.println(String.format("第%d个数为: %d", n, fibByRecursion2(n, cache))); //System.out.println(String.format("第%d个数为: %d", n, fibByLoop(n))); - System.out.println(String.format("第%d个数为: %d", n, fibByDP(n))); + //System.out.println(String.format("第%d个数为: %d", n, fibByDP(n))); n = 5; + cache = new int[n]; //System.out.println(String.format("第%d个数为: %d", n, fibByRecursion(n)); + System.out.println(String.format("第%d个数为: %d", n, fibByRecursion2(n, cache))); //System.out.println(String.format("第%d个数为: %d", n, fibByLoop(n))); - System.out.println(String.format("第%d个数为: %d", n, fibByDP(n))); + //System.out.println(String.format("第%d个数为: %d", n, fibByDP(n))); + n = 6; + cache = new int[n]; //System.out.println(String.format("第%d个数为: %d", n, fibByRecursion(n)); + System.out.println(String.format("第%d个数为: %d", n, fibByRecursion2(n, cache))); //System.out.println(String.format("第%d个数为: %d", n, fibByLoop(n))); - System.out.println(String.format("第%d个数为: %d", n, fibByDP(n))); + //System.out.println(String.format("第%d个数为: %d", n, fibByDP(n))); } /** @@ -55,6 +62,33 @@ private static int fibByRecursion(int n) { return result; } + /** + * 由于递归过程中计算了大量元素, 我们可以使用一个数组cache来对其进行优化 + * + * @param n + * @return + */ + private static int fibByRecursion2(int n, int[] cache) { + if (n == 0) + return 0; + + // 斐波拉契的第1和第2个数为1 + // 临界条件 n = 1 和 n=2 + if (n == 1 || n == 2) { + System.out.println(String.format("往内递归结束:第%d个数", n)); + return 1; + } + + // 如果缓存中不存在,则将计算结果加入到缓存,如果存在,则直接使用缓存 + if (cache[n - 1] == 0) { + System.out.println(String.format("往内递归,第%d个数", n)); + cache[n - 1] = fibByRecursion(n - 1) + fibByRecursion(n - 2); //第n个数为前两个数之后 比如 5 = 3 +2 + } + + System.out.println(String.format("往外递归计算开始: 第%d个数为%d", n, cache[n-1])); + return cache[n - 1]; + } + /** * 使用迭代的方式, 保存之前计算的结果, 用空间换时间 *

@@ -85,6 +119,7 @@ private static int fibByLoop(int n) { } /** + * 动态规划(递归+记忆化(使用数组来存变量)) * 使用斐波拉契来计算动态规划, 使用一个数组来存储整个斐波拉契数列, 这样的做法可以节省大量中间变量的创建, 而且时间复杂度和循环一样 *

* 时间复杂度为O(n), 空间复杂度为O(n) diff --git a/src/main/java/com/study/linkedlist/Reverse.java b/src/main/java/com/study/linkedlist/Reverse.java index 86c3d56..d877c26 100644 --- a/src/main/java/com/study/linkedlist/Reverse.java +++ b/src/main/java/com/study/linkedlist/Reverse.java @@ -33,10 +33,11 @@ public static void main(String[] args) { Printer.printLinkedList(node1); System.out.println(); //Printer.printLinkedList(reverseList(node1)); - //Printer.printLinkedList(reverseList2(node1)); + //Printer.printLinkedList(reverseListByRecursion(node1)); + Printer.printLinkedList(reverseListByRecursion2(node1)); //Printer.printLinkedList(reverseList3(node1)); //Printer.printLinkedList(reverseList4(node1)); - Printer.printLinkedList(reverstListByStack(node1)); + //Printer.printLinkedList(reverstListByStack(node1)); } /** @@ -67,12 +68,12 @@ private static ListNode reverseList(ListNode head) { } - private static ListNode reverseList4(ListNode head){ + private static ListNode reverseList4(ListNode head) { ListNode prev = null; ListNode cur = head; - while(cur != null){ + while (cur != null) { ListNode next = cur.next; cur.next = prev; @@ -104,22 +105,39 @@ private static ListNode reverseList4(ListNode head){ * @param head * @return */ - private static ListNode reverseList2(ListNode head) { - if (head == null || head.next == null) + private static ListNode reverseListByRecursion(ListNode head) { + if (head == null || head.next == null) { + System.out.print("递归往内终止, head = "); + Printer.printLinkedList(head); return head; + } + + System.out.print("递归往内"); + Printer.printLinkedList(head); // 首先进入递归,由于head不为空,且head.next不为空,所以直到最后一个head为5的时候,才满足条件,结束递归,返回prev为5->null,head.next为5->null,head为4->5->null - ListNode prev = reverseList2(head.next); + ListNode prev = reverseListByRecursion(head.next); + System.out.print("递归往外"); + Printer.printLinkedList(head); // 使得head.next.head.next成为一个闭环 // 赋值前4->5->null // 赋值head后 4->5->4->5->4->.....成为一个闭环 // 由于4->5->4成为一个闭环, 这样prev也由5->null变成了5->4->5一个闭环 - head.next.next = head; + head.next.next = head; //修改head的同时, prev也跟着变化 // 将4->5->4闭环剪断 变成4->null // 这样prev也变成了 5->4->null - head.next = null; + head.next = null; //修改head的同时, prev也跟着变化 return prev; } + private static ListNode reverseListByRecursion2(ListNode head) { + if (head == null || head.next == null) { + return head; + } + ListNode prev = reverseListByRecursion2(head.next); + head.next.next = head; + head.next = null; + return prev; + } /** * 借用stack后进先出的原理,对链表进行反转 @@ -155,13 +173,14 @@ private static ListNode reverseList3(ListNode head) { /** * 利用栈的先进后出原理, 实现链表的反转 + * * @param head * @return */ - private static ListNode reverstListByStack(ListNode head){ + private static ListNode reverstListByStack(ListNode head) { // 先将链表每个节点压入栈中, 放入顺序为 1 2 3 4 5 Stack stack = new Stack(); - while(head !=null){ + while (head != null) { stack.push(new ListNode(head.val));// 将节点压栈的时候需要放入没有后继的节点 head = head.next; } diff --git a/src/main/java/com/study/number/FactorialN.java b/src/main/java/com/study/number/FactorialN.java index 6b7ba76..5c282e6 100644 --- a/src/main/java/com/study/number/FactorialN.java +++ b/src/main/java/com/study/number/FactorialN.java @@ -2,7 +2,7 @@ /** * 计算N的阶层 - * + *

* https://time.geekbang.org/course/detail/130-42710 */ public class FactorialN { @@ -24,10 +24,15 @@ public static void main(String[] args) { */ private static int factorialN(int n) { // 递归结束条件是n==1 - if (n == 1) + if (n == 1) { + System.out.println("递归往内结束: n == 1"); return 1; + } // 由于factorialN调用自己, 所以每次进来n都比上一次少1 - return n * factorialN(n - 1); // 通过 n * (n -1) * (n - 2) * .... * 1 + System.out.println("递归往内开始, n == " + n); + int result = n * factorialN(n - 1); // 通过 n * (n -1) * (n - 2) * .... * 1 + System.out.println(String.format("递归往外开始,n = %d, 计算结果: result = %d", n, result)); + return result; } /** diff --git a/src/main/java/com/study/number/KthLargest.java b/src/main/java/com/study/number/KthLargest.java index 42406bb..09878dd 100644 --- a/src/main/java/com/study/number/KthLargest.java +++ b/src/main/java/com/study/number/KthLargest.java @@ -14,6 +14,7 @@ * int k = 3; * int[] arr = [4,5,8,2]; * KthLargest kthLargest = new KthLargest(3, arr); + * 加入的元素必须必数组中最小的元素大,否则无法加入 * kthLargest.add(3); // returns 4 * kthLargest.add(5); // returns 5 * kthLargest.add(10); // returns 5 @@ -21,8 +22,8 @@ * kthLargest.add(4); // returns 8 * 说明: * 你可以假设 nums 的长度≥ k-1 且k ≥ 1。 - * - * + *

+ *

* https://leetcode-cn.com/problems/kth-largest-element-in-a-stream/comments/ */ public class KthLargest { @@ -42,15 +43,15 @@ public KthLargest(int k, int[] nums) { } /** - * 往队列中添加新的元素 + * 往队列中添加新的元素, 只有新元素比堆中最小元素还小才能加入并移除最小元素 * * @param element - * @return + * @return 最小元素 */ public int add(int element) { //如果队列中的元素没有满, 则往里添加 if (queue.size() < k) { - queue.offer(element); + queue.offer(element);// 将元素加到末尾 } else { // 如果队列中的元素满了, 则判断队列最小的元素(顶端的元素)是否比新元素小 // 如果最小的元素比该元素小, 那将顶端最小的元素移除, 将新元素插入 @@ -59,7 +60,22 @@ public int add(int element) { queue.offer(element);//插入新元素 } } - return queue.peek(); //返回最小的元素 + return queue.peek(); //返回顶部最小的元素 + } + + + public int add2(int element){ + if(queue.size() < 3){ + queue.offer(element); + } + else{ + // 只有当添加元素大于堆中最小元素才可以加入 + if(queue.peek() < element){ + queue.poll(); //移除最小元素 + queue.offer(element); //加入新元素 + } + } + return queue.peek();// 返回 } /** @@ -69,14 +85,14 @@ public int add(int element) { */ public static void main(String[] args) { int k = 3; - int[] arr = {8, 5, 4, 2}; + int[] arr = {4, 5, 8, 2}; KthLargest kthLargest = new KthLargest(k, arr); // 每次加入新元素都返回第k大的元素 - System.out.println(kthLargest.add(8)); // 第k大的元素为5 - System.out.println(kthLargest.add(6)); // 第k大的元素为6 - System.out.println(kthLargest.add(4)); // 第k大的元素为6 - System.out.println(kthLargest.add(10)); // 第k大的元素为8 - System.out.println(kthLargest.add(9)); // 第k大的元素为8 + System.out.println(kthLargest.add(3)); // 第3大的元素为4 堆中元素为 4 5 8 + System.out.println(kthLargest.add(5)); // 第3大的元素为5 堆中元素为 5 8 5 + System.out.println(kthLargest.add(10)); // 第3大的元素为5 堆中元素为 5 8 5 + System.out.println(kthLargest.add(9)); // 第3大的元素为8 堆中元素为 8 10 9 + System.out.println(kthLargest.add(4)); // 第3大的元素为8 堆中元素为 8 10 9 } } diff --git a/src/main/java/com/study/number/MajorityElement.java b/src/main/java/com/study/number/MajorityElement.java index e3cfab7..21c373f 100644 --- a/src/main/java/com/study/number/MajorityElement.java +++ b/src/main/java/com/study/number/MajorityElement.java @@ -23,6 +23,20 @@ * 链接:https://leetcode-cn.com/problems/majority-element */ public class MajorityElement { + + /** + 解法: 1. 暴力解法, 循环两次, 第一次循环每个元素, 第二次循环统计当前元素在整个数组中出现的个数,并判断是否大于数组长度的1/2,如果大于就返回该元素. 时间复杂度为 O(n2) + 2. 使用hashmap存储每个元素的数量, 由于hashmap读写的时间复杂度为O(1), 主要时间花费在一层循环上. 时间复杂度为O(n) + 3. 对数组进行排序, 排完序后, 如果题目说一定存在某个元素个数大于长度的一半,那么这个元素肯定会在数组的中间位置. 时间复杂度为O(nlogn) + 4. 利用递归 + 分治的方式,将数组分成左右两个数组,分别从两个数组中获取众数, 如果两边众数相等,则直接返回该众数. 如果众数不相等, 则计算两个众数的个数, 取多的那个. 时间复杂度 O(nlogn) + 5. Boyer-Moore 投票算法 + 原理: 利用众数比非众数多的优势. 众数肯定比非众数多, 所以众数和非众数抵消后,剩下的必然是众数 + 只需要对原数组进行两趟扫描,并且简单易实现。第一趟扫描我们得到一个候选节点candidate,第二趟扫描我们判断candidate出现的次数是否大于⌊ n/2 ⌋。 + 第一趟扫描中,我们需要记录2个值: candidate,初值可以为任何数. count,初值为0 + 之后,对于数组中每一个元素,首先判断count是否为0,若为0,则把candidate设置为当前元素。之后判断candidate是否与当前元素相等,若相等则count+=1,否则count-=1。 + 时间复杂度为O(n),空间复杂度为O(1). + * @param args + */ public static void main(String[] args) { //int[] nums = {3, 2, 3}; //int[] nums = {1}; diff --git a/src/main/java/com/study/number/MaxOneAndTwo.java b/src/main/java/com/study/number/MaxOneAndTwo.java index d0d809c..e780e95 100644 --- a/src/main/java/com/study/number/MaxOneAndTwo.java +++ b/src/main/java/com/study/number/MaxOneAndTwo.java @@ -7,13 +7,14 @@ public class MaxOneAndTwo { public static void main(String[] args) { int[] a = {12, 23, 9, 24, 15, 3, 18}; - int[] result = findMaxOneAndTwo(a); + //int[] result = findMaxOneAndTwo(a); + int[] result = findMax1AndMax2(a); System.out.printf("max1:%d, max2:%d", result[0], result[1]); } /** - * 找出数组中最大和第二大的数 + * 找出数组中最大和第二大的数, O1的时间复杂度 * * @param arr * @return @@ -21,7 +22,7 @@ public static void main(String[] args) { private static int[] findMaxOneAndTwo(int[] arr) { // 定义最大数和第二大数 int max1 = arr[0]; - int max2 = max1; + int max2 = arr[0]; // 遍历数组, 将每个元素与最大数进行比较,如果比最大数大,先将最大数赋给第二大,然后再其赋给最大数. for (int i = 0; i < arr.length - 1; i++) { if (arr[i + 1] > max1) { @@ -29,7 +30,23 @@ private static int[] findMaxOneAndTwo(int[] arr) { max1 = arr[i + 1]; } } - + // 循环完成后 得到最大值和第二大值 return new int[]{max1, max2}; } + + + private static int[] findMax1AndMax2(int[] arr){ + // 假设第一个元素为最大和第二大 + int max1 = arr[0]; + int max2 = max1; + for(int i =0; i < arr.length -1; i++){ + // 从第二个元素开始判断 + if(arr[i+1] > max1){ + // 如果后一个元素比最大的还大,那就将最大的改成第二大,后一个元素改为最大 + max2 = max1; + max1 = arr[i+1]; + } + } + return new int[]{max1,max2}; + } } diff --git a/src/main/java/com/study/number/PowXN.java b/src/main/java/com/study/number/PowXN.java index cbd67d5..5cf8e79 100644 --- a/src/main/java/com/study/number/PowXN.java +++ b/src/main/java/com/study/number/PowXN.java @@ -26,6 +26,13 @@ */ public class PowXN { + /** + * 解法: 一种是暴力解法, 利用循环或者递归的方式 将x乘以自身n次. 时间复杂度O(n) + * 另一种是用递归+分治的解法, 将递归的次数减少到logN次. + * 注意: 需要考虑n为负数和奇偶数的情况. + * + * @param args + */ public static void main(String[] args) { double result = myPow(2.1000, 10); System.out.println(result); @@ -40,11 +47,12 @@ public static void main(String[] args) { // result = myPowRecursion(2.1000, 10); // System.out.println(result); -// result = myPowRecursion(2, 10); -// System.out.println(result); - - result = myPowRecursion2(2, 10); + //result = myPowRecursion(2, 10); + result = myPowRecursion(2.1000, 10); System.out.println(result); + +// result = myPowRecursion2(2, 10); +// System.out.println(result); } /** @@ -143,6 +151,8 @@ public static double myPowRecursion(double x, int n) { N = -N; } return fastPow(x, N); + //return fastPow2(x, N); + //return fastPowByLoop(x, N); } public static double fastPow(double x, long n) { @@ -155,6 +165,7 @@ public static double fastPow(double x, long n) { // 使用递归, 将循环乘以x的次数减少到logn次 (类似于二分查找) double half = fastPow(x, n / 2);//每次递归的n都是上一次的1/2 double beforeHalf = half; + // 每一次执行会将上一递归的half结果拿来相乘,这样每次的half都是上一次的half的平方或者平方乘以x // 需要考虑n为偶数和奇数的情况 if (n % 2 == 0) { half = half * half; @@ -166,9 +177,46 @@ public static double fastPow(double x, long n) { return half; } +// /** +// * 迭代实现分治的算法有问题, 需要修改 +// * @param x +// * @param n +// * @return +// */ +//// public static double fastPowByLoop(double x, long n) { +// double result = x; +// while (n >= 2) { +// if (n % 2 == 0) { +// result = result * result; +// } else { +// result = result * result * x; +// } +// // n = 1的时候 n /2 没有任何意义 +// n = n / 2; +// } +// return result; +// } + + + public static double fastPow2(double x, long n) { + if (n == 0) { + return 1.0; + } + + double half = fastPow2(x, n / 2); + + if (n % 2 == 0) { + half = half * half; + } else { + half = half * half * x; + } + return half; + } + /** * 暴利法(递归) 利用递归的方式将 x乘以自己n次, 效果和迭代一样. 时间复杂度O(n) + * * @param x * @param n * @return @@ -183,13 +231,13 @@ public static double myPowRecursion2(double x, int n) { return slowPow(x, N); } - public static double slowPow(double x, long n){ - if(n == 0){ + public static double slowPow(double x, long n) { + if (n == 0) { return 1.0; } // 从外往内 找终止条件 - double result = slowPow(x , n-1); + double result = slowPow(x, n - 1); // 从内往外递归 真正开始计算 result *= x; diff --git a/src/main/java/com/study/number/Prime.java b/src/main/java/com/study/number/Prime.java index e351ce1..0b8d1fe 100644 --- a/src/main/java/com/study/number/Prime.java +++ b/src/main/java/com/study/number/Prime.java @@ -9,12 +9,15 @@ public class Prime { public static void main(String[] args) { int a = 2; //int a = 11; + //int a = 4; //System.out.println(a + (isPrime(a) ? "是" : "不是") + "质数"); //System.out.println(a + (isPrime2(a) ? "是": "不是") + "质数"); - System.out.println(a + (isPrime3(a) ? "是" : "不是") + "质数"); + System.out.println(a + (isPrime2_1(a) ? "是": "不是") + "质数"); + + //System.out.println(a + (isPrime3(a) ? "是" : "不是") + "质数"); } public static boolean isPrime(int n) { @@ -44,26 +47,46 @@ public static boolean isPrime2(int n) { return true; } - /** - * 优化后的算法, 只需要判断从2到该数的根号之前的整数即可 + * 判断n是否为质数, 只要n对任何2-n的数取余都不为0即可 * * @param n * @return */ - public static boolean isPrime3(int n) { - // 1不是质数 - if (n <= 1) { - return false; - } + private static boolean isPrime2_1(int n) { + if (n <= 1) + return false; - // 将n开根号 - double s = Math.sqrt(n); - // 如果 2到n的根号数之内的数 都不能被n整除, 那么n为质数 - for (int i = 2; i <= s; i++) { + // 遍历2到n的所有元素, 如果有n能取余为0的元素, 那么n都不是质数 + for (int i = 2; i < n; i++) { if (n % i == 0) return false; } return true; } + + + /** + * 优化后的算法, 只需要判断从2到该数的根号之前的整数即可 + * 求开方根 会导致结果不准 + * + * @param n + * @return + */ +// public static boolean isPrime3(int n) { +// // 1不是质数 +// if (n <= 1) { +// return false; +// } +// +// // 求开方根 会导致结果不准 +// // 将n开根号 +// double s = Math.sqrt(n); +// // 如果 2到n的根号数之内的数 都不能被n整除, 那么n为质数 +// for (int i = 2; i <= s; i++) { +// if (n % i == 0) +// return false; +// } +// return true; +// } } diff --git a/src/main/java/com/study/array/SameNumberPossition.java b/src/main/java/com/study/number/SameNumberPossition.java similarity index 67% rename from src/main/java/com/study/array/SameNumberPossition.java rename to src/main/java/com/study/number/SameNumberPossition.java index 04b52c0..2dc7576 100644 --- a/src/main/java/com/study/array/SameNumberPossition.java +++ b/src/main/java/com/study/number/SameNumberPossition.java @@ -1,4 +1,4 @@ -package com.study.array; +package com.study.number; /** * 求数组中连续数第一次出现的位置 @@ -10,6 +10,11 @@ public static void main(String[] args) { System.out.println(position(arr)); } + /** + * 需要判断上一个索引位置和当前位置的元素是否相等,如果相等说明该元素连续出现了,返回该索引位置 + * @param arr + * @return + */ private static int position(int[] arr) { for (int i = 0; i < arr.length - 1; i++) { if (arr[i] == arr[i + 1]) { diff --git a/src/main/java/com/study/number/SumofArithmeticSequences.java b/src/main/java/com/study/number/SumofArithmeticSequences.java index de47673..2bba7e6 100644 --- a/src/main/java/com/study/number/SumofArithmeticSequences.java +++ b/src/main/java/com/study/number/SumofArithmeticSequences.java @@ -19,9 +19,14 @@ public static void main(String[] args) { */ private static int sum(int n) { // 当n=1的时候 结束迭代 - if (n == 1) + if (n == 1){ + System.out.println("递归往内终止, n = 1"); return 1; - return n + sum(n - 1); // n + (n-1) + (n-2) + ... + 1; + } + System.out.println("递归往内直到终止条件, n = " + n); + int result = n + sum(n - 1); // n + (n-1) + (n-2) + ... + 1; + System.out.println(String.format("开始真正计算结果, n= %d, result = %d", n, result)); + return result; } /** diff --git a/src/main/java/com/study/number/ThreeSum.java b/src/main/java/com/study/number/ThreeSum.java index af516fe..a223a90 100644 --- a/src/main/java/com/study/number/ThreeSum.java +++ b/src/main/java/com/study/number/ThreeSum.java @@ -23,13 +23,13 @@ */ public class ThreeSum { public static void main(String[] args) { - //int[] nums = {-1, 0, 1, 2, -1, -4}; - //int[] nums = {-1, 0, 1, 0, 0}; - //int[] nums = {-1, 0, 1, 2, -1, -4}; - int[] nums = {82597, -9243, 62390, 83030, -97960, -26521, -61011, 83390, -38677, 12333, 75987, 46091, 83794, 19355, -71037, -6242, -28801, 324, 1202, -90885, -2989, -95597, -34333, 35528, 5680, 89093, -90606, 50360, -29393, -27012, 53313, 65213, 99818, -82405, -41661, -3333, -51952, 72135, -1523, 26377, 74685, 96992, 92263, 15929, 5467, -99555, -43348, -41689, -60383, -3990, 32165, 65265, -72973, -58372, 12741, -48568, -46596, 72419, -1859, 34153, 62937, 81310, -61823, -96770, -54944, 8845, -91184, 24208, -29078, 31495, 65258, 14198, 85395, 70506, -40908, 56740, -12228, -40072, 32429, 93001, 68445, -73927, 25731, -91859, -24150, 10093, -60271, -81683, -18126, 51055, 48189, -6468, 25057, 81194, -58628, 74042, 66158, -14452, -49851, -43667, 11092, 39189, -17025, -79173, 13606, 83172, 92647, -59741, 19343, -26644, -57607, 82908, -20655, 1637, 80060, 98994, 39331, -31274, -61523, 91225, -72953, 13211, -75116, -98421, -41571, -69074, 99587, 39345, 42151, -2460, 98236, 15690, -52507, -95803, -48935, -46492, -45606, -79254, -99851, 52533, 73486, 39948, -7240, 71815, -585, -96252, 90990, -93815, 93340, -71848, 58733, -14859, -83082, -75794, -82082, -24871, -15206, 91207, -56469, -93618, 67131, -8682, 75719, 87429, -98757, -7535, -24890, -94160, 85003, 33928, 75538, 97456, -66424, -60074, -8527, -28697, -22308, 2246, -70134, -82319, -10184, 87081, -34949, -28645, -47352, -83966, -60418, -15293, -53067, -25921, 55172, 75064, 95859, 48049, 34311, -86931, -38586, 33686, -36714, 96922, 76713, -22165, -80585, -34503, -44516, 39217, -28457, 47227, -94036, 43457, 24626, -87359, 26898, -70819, 30528, -32397, -69486, 84912, -1187, -98986, -32958, 4280, -79129, -65604, 9344, 58964, 50584, 71128, -55480, 24986, 15086, -62360, -42977, -49482, -77256, -36895, -74818, 20, 3063, -49426, 28152, -97329, 6086, 86035, -88743, 35241, 44249, 19927, -10660, 89404, 24179, -26621, -6511, 57745, -28750, 96340, -97160, -97822, -49979, 52307, 79462, 94273, -24808, 77104, 9255, -83057, 77655, 21361, 55956, -9096, 48599, -40490, -55107, 2689, 29608, 20497, 66834, -34678, 23553, -81400, -66630, -96321, -34499, -12957, -20564, 25610, -4322, -58462, 20801, 53700, 71527, 24669, -54534, 57879, -3221, 33636, 3900, 97832, -27688, -98715, 5992, 24520, -55401, -57613, -69926, 57377, -77610, 20123, 52174, 860, 60429, -91994, -62403, -6218, -90610, -37263, -15052, 62069, -96465, 44254, 89892, -3406, 19121, -41842, -87783, -64125, -56120, 73904, -22797, -58118, -4866, 5356, 75318, 46119, 21276, -19246, -9241, -97425, 57333, -15802, 93149, 25689, -5532, 95716, 39209, -87672, -29470, -16324, -15331, 27632, -39454, 56530, -16000, 29853, 46475, 78242, -46602, 83192, -73440, -15816, 50964, -36601, 89758, 38375, -40007, -36675, -94030, 67576, 46811, -64919, 45595, 76530, 40398, 35845, 41791, 67697, -30439, -82944, 63115, 33447, -36046, -50122, -34789, 43003, -78947, -38763, -89210, 32756, -20389, -31358, -90526, -81607, 88741, 86643, 98422, 47389, -75189, 13091, 95993, -15501, 94260, -25584, -1483, -67261, -70753, 25160, 89614, -90620, -48542, 83889, -12388, -9642, -37043, -67663, 28794, -8801, 13621, 12241, 55379, 84290, 21692, -95906, -85617, -17341, -63767, 80183, -4942, -51478, 30997, -13658, 8838, 17452, -82869, -39897, 68449, 31964, 98158, -49489, 62283, -62209, -92792, -59342, 55146, -38533, 20496, 62667, 62593, 36095, -12470, 5453, -50451, 74716, -17902, 3302, -16760, -71642, -34819, 96459, -72860, 21638, 47342, -69897, -40180, 44466, 76496, 84659, 13848, -91600, -90887, -63742, -2156, -84981, -99280, 94326, -33854, 92029, -50811, 98711, -36459, -75555, 79110, -88164, -97397, -84217, 97457, 64387, 30513, -53190, -83215, 252, 2344, -27177, -92945, -89010, 82662, -11670, 86069, 53417, 42702, 97082, 3695, -14530, -46334, 17910, 77999, 28009, -12374, 15498, -46941, 97088, -35030, 95040, 92095, -59469, -24761, 46491, 67357, -66658, 37446, -65130, -50416, 99197, 30925, 27308, 54122, -44719, 12582, -99525, -38446, -69050, -22352, 94757, -56062, 33684, -40199, -46399, 96842, -50881, -22380, -65021, 40582, 53623, -76034, 77018, -97074, -84838, -22953, -74205, 79715, -33920, -35794, -91369, 73421, -82492, 63680, -14915, -33295, 37145, 76852, -69442, 60125, -74166, 74308, -1900, -30195, -16267, -60781, -27760, 5852, 38917, 25742, -3765, 49097, -63541, 98612, -92865, -30248, 9612, -8798, 53262, 95781, -42278, -36529, 7252, -27394, -5021, 59178, 80934, -48480, -75131, -54439, -19145, -48140, 98457, -6601, -51616, -89730, 78028, 32083, -48904, 16822, -81153, -8832, 48720, -80728, -45133, -86647, -4259, -40453, 2590, 28613, 50523, -4105, -27790, -74579, -17223, 63721, 33489, -47921, 97628, -97691, -14782, -65644, 18008, -93651, -71266, 80990, -76732, -47104, 35368, 28632, 59818, -86269, -89753, 34557, -92230, -5933, -3487, -73557, -13174, -43981, -43630, -55171, 30254, -83710, -99583, -13500, 71787, 5017, -25117, -78586, 86941, -3251, -23867, -36315, 75973, 86272, -45575, 77462, -98836, -10859, 70168, -32971, -38739, -12761, 93410, 14014, -30706, -77356, -85965, -62316, 63918, -59914, -64088, 1591, -10957, 38004, 15129, -83602, -51791, 34381, -89382, -26056, 8942, 5465, 71458, -73805, -87445, -19921, -80784, 69150, -34168, 28301, -68955, 18041, 6059, 82342, 9947, 39795, 44047, -57313, 48569, 81936, -2863, -80932, 32976, -86454, -84207, 33033, 32867, 9104, -16580, -25727, 80157, -70169, 53741, 86522, 84651, 68480, 84018, 61932, 7332, -61322, -69663, 76370, 41206, 12326, -34689, 17016, 82975, -23386, 39417, 72793, 44774, -96259, 3213, 79952, 29265, -61492, -49337, 14162, 65886, 3342, -41622, -62659, -90402, -24751, 88511, 54739, -21383, -40161, -96610, -24944, -602, -76842, -21856, 69964, 43994, -15121, -85530, 12718, 13170, -13547, 69222, 62417, -75305, -81446, -38786, -52075, -23110, 97681, -82800, -53178, 11474, 35857, 94197, -58148, -23689, 32506, 92154, -64536, -73930, -77138, 97446, -83459, 70963, 22452, 68472, -3728, -25059, -49405, 95129, -6167, 12808, 99918, 30113, -12641, -26665, 86362, -33505, 50661, 26714, 33701, 89012, -91540, 40517, -12716, -57185, -87230, 29914, -59560, 13200, -72723, 58272, 23913, -45586, -96593, -26265, -2141, 31087, 81399, 92511, -34049, 20577, 2803, 26003, 8940, 42117, 40887, -82715, 38269, 40969, -50022, 72088, 21291, -67280, -16523, 90535, 18669, 94342, -39568, -88080, -99486, -20716, 23108, -28037, 63342, 36863, -29420, -44016, 75135, 73415, 16059, -4899, 86893, 43136, -7041, 33483, -67612, 25327, 40830, 6184, 61805, 4247, 81119, -22854, -26104, -63466, 63093, -63685, 60369, 51023, 51644, -16350, 74438, -83514, 99083, 10079, -58451, -79621, 48471, 67131, -86940, 99093, 11855, -22272, -67683, -44371, 9541, 18123, 37766, -70922, 80385, -57513, -76021, -47890, 36154, 72935, 84387, -92681, -88303, -7810, 59902, -90, -64704, -28396, -66403, 8860, 13343, 33882, 85680, 7228, 28160, -14003, 54369, -58893, 92606, -63492, -10101, 64714, 58486, 29948, -44679, -22763, 10151, -56695, 4031, -18242, -36232, 86168, -14263, 9883, 47124, 47271, 92761, -24958, -73263, -79661, -69147, -18874, 29546, -92588, -85771, 26451, -86650, -43306, -59094, -47492, -34821, -91763, -47670, 33537, 22843, 67417, -759, 92159, 63075, 94065, -26988, 55276, 65903, 30414, -67129, -99508, -83092, -91493, -50426, 14349, -83216, -76090, 32742, -5306, -93310, -60750, -60620, -45484, -21108, -58341, -28048, -52803, 69735, 78906, 81649, 32565, -86804, -83202, -65688, -1760, 89707, 93322, -72750, 84134, 71900, -37720, 19450, -78018, 22001, -23604, 26276, -21498, 65892, -72117, -89834, -23867, 55817, -77963, 42518, 93123, -83916, 63260, -2243, -97108, 85442, -36775, 17984, -58810, 99664, -19082, 93075, -69329, 87061, 79713, 16296, 70996, 13483, -74582, 49900, -27669, -40562, 1209, -20572, 34660, 83193, 75579, 7344, 64925, 88361, 60969, 3114, 44611, -27445, 53049, -16085, -92851, -53306, 13859, -33532, 86622, -75666, -18159, -98256, 51875, -42251, -27977, -18080, 23772, 38160, 41779, 9147, 94175, 99905, -85755, 62535, -88412, -52038, -68171, 93255, -44684, -11242, -104, 31796, 62346, -54931, -55790, -70032, 46221, 56541, -91947, 90592, 93503, 4071, 20646, 4856, -63598, 15396, -50708, 32138, -85164, 38528, -89959, 53852, 57915, -42421, -88916, -75072, 67030, -29066, 49542, -71591, 61708, -53985, -43051, 28483, 46991, -83216, 80991, -46254, -48716, 39356, -8270, -47763, -34410, 874, -1186, -7049, 28846, 11276, 21960, -13304, -11433, -4913, 55754, 79616, 70423, -27523, 64803, 49277, 14906, -97401, -92390, 91075, 70736, 21971, -3303, 55333, -93996, 76538, 54603, -75899, 98801, 46887, 35041, 48302, -52318, 55439, 24574, 14079, -24889, 83440, 14961, 34312, -89260, -22293, -81271, -2586, -71059, -10640, -93095, -5453, -70041, 66543, 74012, -11662, -52477, -37597, -70919, 92971, -17452, -67306, -80418, 7225, -89296, 24296, 86547, 37154, -10696, 74436, -63959, 58860, 33590, -88925, -97814, -83664, 85484, -8385, -50879, 57729, -74728, -87852, -15524, -91120, 22062, 28134, 80917, 32026, 49707, -54252, -44319, -35139, 13777, 44660, 85274, 25043, 58781, -89035, -76274, 6364, -63625, 72855, 43242, -35033, 12820, -27460, 77372, -47578, -61162, -70758, -1343, -4159, 64935, 56024, -2151, 43770, 19758, -30186, -86040, 24666, -62332, -67542, 73180, -25821, -27826, -45504, -36858, -12041, 20017, -24066, -56625, -52097, -47239, -90694, 8959, 7712, -14258, -5860, 55349, 61808, -4423, -93703, 64681, -98641, -25222, 46999, -83831, -54714, 19997, -68477, 66073, 51801, -66491, 52061, -52866, 79907, -39736, -68331, 68937, 91464, 98892, 910, 93501, 31295, -85873, 27036, -57340, 50412, 21, -2445, 29471, 71317, 82093, -94823, -54458, -97410, 39560, -7628, 66452, 39701, 54029, 37906, 46773, 58296, 60370, -61090, 85501, -86874, 71443, -72702, -72047, 14848, 34102, 77975, -66294, -36576, 31349, 52493, -70833, -80287, 94435, 39745, -98291, 84524, -18942, 10236, 93448, 50846, 94023, -6939, 47999, 14740, 30165, 81048, 84935, -19177, -13594, 32289, 62628, -90612, -542, -66627, 64255, 71199, -83841, -82943, -73885, 8623, -67214, -9474, -35249, 62254, -14087, -90969, 21515, -83303, 94377, -91619, 19956, -98810, 96727, -91939, 29119, -85473, -82153, -69008, 44850, 74299, -76459, -86464, 8315, -49912, -28665, 59052, -69708, 76024, -92738, 50098, 18683, -91438, 18096, -19335, 35659, 91826, 15779, -73070, 67873, -12458, -71440, -46721, 54856, 97212, -81875, 35805, 36952, 68498, 81627, -34231, 81712, 27100, -9741, -82612, 18766, -36392, 2759, 41728, 69743, 26825, 48355, -17790, 17165, 56558, 3295, -24375, 55669, -16109, 24079, 73414, 48990, -11931, -78214, 90745, 19878, 35673, -15317, -89086, 94675, -92513, 88410, -93248, -19475, -74041, -19165, 32329, -26266, -46828, -18747, 45328, 8990, -78219, -25874, -74801, -44956, -54577, -29756, -99822, -35731, -18348, -68915, -83518, -53451, 95471, -2954, -13706, -8763, -21642, -37210, 16814, -60070, -42743, 27697, -36333, -42362, 11576, 85742, -82536, 68767, -56103, -63012, 71396, -78464, -68101, -15917, -11113, -3596, 77626, -60191, -30585, -73584, 6214, -84303, 18403, 23618, -15619, -89755, -59515, -59103, -74308, -63725, -29364, -52376, -96130, 70894, -12609, 50845, -2314, 42264, -70825, 64481, 55752, 4460, -68603, -88701, 4713, -50441, -51333, -77907, 97412, -66616, -49430, 60489, -85262, -97621, -18980, 44727, -69321, -57730, 66287, -92566, -64427, -14270, 11515, -92612, -87645, 61557, 24197, -81923, -39831, -10301, -23640, -76219, -68025, 92761, -76493, 68554, -77734, -95620, -11753, -51700, 98234, -68544, -61838, 29467, 46603, -18221, -35441, 74537, 40327, -58293, 75755, -57301, -7532, -94163, 18179, -14388, -22258, -46417, -48285, 18242, -77551, 82620, 250, -20060, -79568, -77259, 82052, -98897, -75464, 48773, -79040, -11293, 45941, -67876, -69204, -46477, -46107, 792, 60546, -34573, -12879, -94562, 20356, -48004, -62429, 96242, 40594, 2099, 99494, 25724, -39394, -2388, -18563, -56510, -83570, -29214, 3015, 74454, 74197, 76678, -46597, 60630, -76093, 37578, -82045, -24077, 62082, -87787, -74936, 58687, 12200, -98952, 70155, -77370, 21710, -84625, -60556, -84128, 925, 65474, -15741, -94619, 88377, 89334, 44749, 22002, -45750, -93081, -14600, -83447, 46691, 85040, -66447, -80085, 56308, 44310, 24979, -29694, 57991, 4675, -71273, -44508, 13615, -54710, 23552, -78253, -34637, 50497, 68706, 81543, -88408, -21405, 6001, -33834, -21570, -46692, -25344, 20310, 71258, -97680, 11721, 59977, 59247, -48949, 98955, -50276, -80844, -27935, -76102, 55858, -33492, 40680, 66691, -33188, 8284, 64893, -7528, 6019, -85523, 8434, -64366, -56663, 26862, 30008, -7611, -12179, -70076, 21426, -11261, -36864, -61937, -59677, 929, -21052, 3848, -20888, -16065, 98995, -32293, -86121, -54564, 77831, 68602, 74977, 31658, 40699, 29755, 98424, 80358, -69337, 26339, 13213, -46016, -18331, 64713, -46883, -58451, -70024, -92393, -4088, 70628, -51185, 71164, -75791, -1636, -29102, -16929, -87650, -84589, -24229, -42137, -15653, 94825, 13042, 88499, -47100, -90358, -7180, 29754, -65727, -42659, -85560, -9037, -52459, 20997, -47425, 17318, 21122, 20472, -23037, 65216, -63625, -7877, -91907, 24100, -72516, 22903, -85247, -8938, 73878, 54953, 87480, -31466, -99524, 35369, -78376, 89984, -15982, 94045, -7269, 23319, -80456, -37653, -76756, 2909, 81936, 54958, -12393, 60560, -84664, -82413, 66941, -26573, -97532, 64460, 18593, -85789, -38820, -92575, -43663, -89435, 83272, -50585, 13616, -71541, -53156, 727, -27644, 16538, 34049, 57745, 34348, 35009, 16634, -18791, 23271, -63844, 95817, 21781, 16590, 59669, 15966, -6864, 48050, -36143, 97427, -59390, 96931, 78939, -1958, 50777, 43338, -51149, 39235, -27054, -43492, 67457, -83616, 37179, 10390, 85818, 2391, 73635, 87579, -49127, -81264, -79023, -81590, 53554, -74972, -83940, -13726, -39095, 29174, 78072, 76104, 47778, 25797, -29515, -6493, -92793, 22481, -36197, -65560, 42342, 15750, 97556, 99634, -56048, -35688, 13501, 63969, -74291, 50911, 39225, 93702, -3490, -59461, -30105, -46761, -80113, 92906, -68487, 50742, 36152, -90240, -83631, 24597, -50566, -15477, 18470, 77038, 40223, -80364, -98676, 70957, -63647, 99537, 13041, 31679, 86631, 37633, -16866, 13686, -71565, 21652, -46053, -80578, -61382, 68487, -6417, 4656, 20811, 67013, -30868, -11219, 46, 74944, 14627, 56965, 42275, -52480, 52162, -84883, -52579, -90331, 92792, 42184, -73422, -58440, 65308, -25069, 5475, -57996, 59557, -17561, 2826, -56939, 14996, -94855, -53707, 99159, 43645, -67719, -1331, 21412, 41704, 31612, 32622, 1919, -69333, -69828, 22422, -78842, 57896, -17363, 27979, -76897, 35008, 46482, -75289, 65799, 20057, 7170, 41326, -76069, 90840, -81253, -50749, 3649, -42315, 45238, -33924, 62101, 96906, 58884, -7617, -28689, -66578, 62458, 50876, -57553, 6739, 41014, -64040, -34916, 37940, 13048, -97478, -11318, -89440, -31933, -40357, -59737, -76718, -14104, -31774, 28001, 4103, 41702, -25120, -31654, 63085, -3642, 84870, -83896, -76422, -61520, 12900, 88678, 85547, 33132, -88627, 52820, 63915, -27472, 78867, -51439, 33005, -23447, -3271, -39308, 39726, -74260, -31874, -36893, 93656, 910, -98362, 60450, -88048, 99308, 13947, 83996, -90415, -35117, 70858, -55332, -31721, 97528, 82982, -86218, 6822, 25227, 36946, 97077, -4257, -41526, 56795, 89870, 75860, -70802, 21779, 14184, -16511, -89156, -31422, 71470, 69600, -78498, 74079, -19410, 40311, 28501, 26397, -67574, -32518, 68510, 38615, 19355, -6088, -97159, -29255, -92523, 3023, -42536, -88681, 64255, 41206, 44119, 52208, 39522, -52108, 91276, -70514, 83436, 63289, -79741, 9623, 99559, 12642, 85950, 83735, -21156, -67208, 98088, -7341, -27763, -30048, -44099, -14866, -45504, -91704, 19369, 13700, 10481, -49344, -85686, 33994, 19672, 36028, 60842, 66564, -24919, 33950, -93616, -47430, -35391, -28279, 56806, 74690, 39284, -96683, -7642, -75232, 37657, -14531, -86870, -9274, -26173, 98640, 88652, 64257, 46457, 37814, -19370, 9337, -22556, -41525, 39105, -28719, 51611, -93252, 98044, -90996, 21710, -47605, -64259, -32727, 53611, -31918, -3555, 33316, -66472, 21274, -37731, -2919, 15016, 48779, -88868, 1897, 41728, 46344, -89667, 37848, 68092, -44011, 85354, -43776, 38739, -31423, -66330, 65167, -22016, 59405, 34328, -60042, 87660, -67698, -59174, -1408, -46809, -43485, -88807, -60489, 13974, 22319, 55836, -62995, -37375, -4185, 32687, -36551, -75237, 58280, 26942, -73756, 71756, 78775, -40573, 14367, -71622, -77338, 24112, 23414, -7679, -51721, 87492, 85066, -21612, 57045, 10673, -96836, 52461, -62218, -9310, 65862, -22748, 89906, -96987, -98698, 26956, -43428, 46141, 47456, 28095, 55952, 67323, -36455, -60202, -43302, -82932, 42020, 77036, 10142, 60406, 70331, 63836, 58850, -66752, 52109, 21395, -10238, -98647, -41962, 27778, 69060, 98535, -28680, -52263, -56679, 66103, -42426, 27203, 80021, 10153, 58678, 36398, 63112, 34911, 20515, 62082, -15659, -40785, 27054, 43767, -20289, 65838, -6954, -60228, -72226, 52236, -35464, 25209, -15462, -79617, -41668, -84083, 62404, -69062, 18913, 46545, 20757, 13805, 24717, -18461, -47009, -25779, 68834, 64824, 34473, 39576, 31570, 14861, -15114, -41233, 95509, 68232, 67846, 84902, -83060, 17642, -18422, 73688, 77671, -26930, 64484, -99637, 73875, 6428, 21034, -73471, 19664, -68031, 15922, -27028, 48137, 54955, -82793, -41144, -10218, -24921, -28299, -2288, 68518, -54452, 15686, -41814, 66165, -72207, -61986, 80020, 50544, -99500, 16244, 78998, 40989, 14525, -56061, -24692, -94790, 21111, 37296, -90794, 72100, 70550, -31757, 17708, -74290, 61910, 78039, -78629, -25033, 73172, -91953, 10052, 64502, 99585, -1741, 90324, -73723, 68942, 28149, 30218, 24422, 16659, 10710, -62594, 94249, 96588, 46192, 34251, 73500, -65995, -81168, 41412, -98724, -63710, -54696, -52407, 19746, 45869, 27821, -94866, -76705, -13417, -61995, -71560, 43450, 67384, -8838, -80293, -28937, 23330, -89694, -40586, 46918, 80429, -5475, 78013, 25309, -34162, 37236, -77577, 86744, 26281, -29033, -91813, 35347, 13033, -13631, -24459, 3325, -71078, -75359, 81311, 19700, 47678, -74680, -84113, 45192, 35502, 37675, 19553, 76522, -51098, -18211, 89717, 4508, -82946, 27749, 85995, 89912, -53678, -64727, -14778, 32075, -63412, -40524, 86440, -2707, -36821, 63850, -30883, 67294, -99468, -23708, 34932, 34386, 98899, 29239, -23385, 5897, 54882, 98660, 49098, 70275, 17718, 88533, 52161, 63340, 50061, -89457, 19491, -99156, 24873, -17008, 64610, -55543, 50495, 17056, -10400, -56678, -29073, -42960, -76418, 98562, -88104, -96255, 10159, -90724, 54011, 12052, 45871, -90933, -69420, 67039, 37202, 78051, -52197, -40278, -58425, 65414, -23394, -1415, 6912, -53447, 7352, 17307, -78147, 63727, 98905, 55412, -57658, -32884, -44878, 22755, 39730, 3638, 35111, 39777, 74193, 38736, -11829, -61188, -92757, 55946, -71232, -63032, -83947, 39147, -96684, -99233, 25131, -32197, 24406, -55428, -61941, 25874, -69453, 64483, -19644, -68441, 12783, 87338, -48676, 66451, -447, -61590, 50932, -11270, 29035, 65698, -63544, 10029, 80499, -9461, 86368, 91365, -81810, -71914, -52056, -13782, 44240, -30093, -2437, 24007, 67581, -17365, -69164, -8420, -69289, -29370, 48010, 90439, 13141, 69243, 50668, 39328, 61731, 78266, -81313, 17921, -38196, 55261, 9948, -24970, 75712, -72106, 28696, 7461, 31621, 61047, 51476, 56512, 11839, -96916, -82739, 28924, -99927, 58449, 37280, 69357, 11219, -32119, -62050, -48745, -83486, -52376, 42668, 82659, 68882, 38773, 46269, -96005, 97630, 25009, -2951, -67811, 99801, 81587, -79793, -18547, -83086, 69512, 33127, -92145, -88497, 47703, 59527, 1909, 88785, -88882, 69188, -46131, -5589, -15086, 36255, -53238, -33009, 82664, 53901, 35939, -42946, -25571, 33298, 69291, 53199, 74746, -40127, -39050, 91033, 51717, -98048, 87240, 36172, 65453, -94425, -63694, -30027, 59004, 88660, 3649, -20267, -52565, -67321, 34037, 4320, 91515, -56753, 60115, 27134, 68617, -61395, -26503, -98929, -8849, -63318, 10709, -16151, 61905, -95785, 5262, 23670, -25277, 90206, -19391, 45735, 37208, -31992, -92450, 18516, -90452, -58870, -58602, 93383, 14333, 17994, 82411, -54126, -32576, 35440, -60526, -78764, -25069, -9022, -394, 92186, -38057, 55328, -61569, 67780, 77169, 19546, -92664, -94948, 44484, -13439, 83529, 27518, -48333, 72998, 38342, -90553, -98578, -76906, 81515, -16464, 78439, 92529, 35225, -39968, -10130, -7845, -32245, -74955, -74996, 67731, -13897, -82493, 33407, 93619, 59560, -24404, -57553, 19486, -45341, 34098, -24978, -33612, 79058, 71847, 76713, -95422, 6421, -96075, -59130, -28976, -16922, -62203, 69970, 68331, 21874, 40551, 89650, 51908, 58181, 66480, -68177, 34323, -3046, -49656, -59758, 43564, -10960, -30796, 15473, -20216, 46085, -85355, 41515, -30669, -87498, 57711, 56067, 63199, -83805, 62042, 91213, -14606, 4394, -562, 74913, 10406, 96810, -61595, 32564, 31640, -9732, 42058, 98052, -7908, -72330, 1558, -80301, 34878, 32900, 3939, -8824, 88316, 20937, 21566, -3218, -66080, -31620, 86859, 54289, 90476, -42889, -15016, -18838, 75456, 30159, -67101, 42328, -92703, 85850, -5475, 23470, -80806, 68206, 17764, 88235, 46421, -41578, 74005, -81142, 80545, 20868, -1560, 64017, 83784, 68863, -97516, -13016, -72223, 79630, -55692, 82255, 88467, 28007, -34686, -69049, -41677, 88535, -8217, 68060, -51280, 28971, 49088, 49235, 26905, -81117, -44888, 40623, 74337, -24662, 97476, 79542, -72082, -35093, 98175, -61761, -68169, 59697, -62542, -72965, 59883, -64026, -37656, -92392, -12113, -73495, 98258, 68379, -21545, 64607, -70957, -92254, -97460, -63436, -8853, -19357, -51965, -76582, 12687, -49712, 45413, -60043, 33496, 31539, -57347, 41837, 67280, -68813, 52088, -13155, -86430, -15239, -45030, 96041, 18749, -23992, 46048, 35243, -79450, 85425, -58524, 88781, -39454, 53073, -48864, -82289, 39086, 82540, -11555, 25014, -5431, -39585, -89526, 2705, 31953, -81611, 36985, -56022, 68684, -27101, 11422, 64655, -26965, -63081, -13840, -91003, -78147, -8966, 41488, 1988, 99021, -61575, -47060, 65260, -23844, -21781, -91865, -19607, 44808, 2890, 63692, -88663, -58272, 15970, -65195, -45416, -48444, -78226, -65332, -24568, 42833, -1806, -71595, 80002, -52250, 30952, 48452, -90106, 31015, -22073, 62339, 63318, 78391, 28699, 77900, -4026, -76870, -45943, 33665, 9174, -84360, -22684, -16832, -67949, -38077, -38987, -32847, 51443, -53580, -13505, 9344, -92337, 26585, 70458, -52764, -67471, -68411, -1119, -2072, -93476, 67981, 40887, -89304, -12235, 41488, 1454, 5355, -34855, -72080, 24514, -58305, 3340, 34331, 8731, 77451, -64983, -57876, 82874, 62481, -32754, -39902, 22451, -79095, -23904, 78409, -7418, 77916}; - List> resultList = threeSum(nums); + //int[] nums = {-1, 0, 1, 2, -4}; + //int[] nums = {-1, 0, 1}; + int[] nums = {-1, 0, 1, 2, -2, -4}; + //int[] nums = {82597, -9243, 62390, 83030, -97960, -26521, -61011, 83390, -38677, 12333, 75987, 46091, 83794, 19355, -71037, -6242, -28801, 324, 1202, -90885, -2989, -95597, -34333, 35528, 5680, 89093, -90606, 50360, -29393, -27012, 53313, 65213, 99818, -82405, -41661, -3333, -51952, 72135, -1523, 26377, 74685, 96992, 92263, 15929, 5467, -99555, -43348, -41689, -60383, -3990, 32165, 65265, -72973, -58372, 12741, -48568, -46596, 72419, -1859, 34153, 62937, 81310, -61823, -96770, -54944, 8845, -91184, 24208, -29078, 31495, 65258, 14198, 85395, 70506, -40908, 56740, -12228, -40072, 32429, 93001, 68445, -73927, 25731, -91859, -24150, 10093, -60271, -81683, -18126, 51055, 48189, -6468, 25057, 81194, -58628, 74042, 66158, -14452, -49851, -43667, 11092, 39189, -17025, -79173, 13606, 83172, 92647, -59741, 19343, -26644, -57607, 82908, -20655, 1637, 80060, 98994, 39331, -31274, -61523, 91225, -72953, 13211, -75116, -98421, -41571, -69074, 99587, 39345, 42151, -2460, 98236, 15690, -52507, -95803, -48935, -46492, -45606, -79254, -99851, 52533, 73486, 39948, -7240, 71815, -585, -96252, 90990, -93815, 93340, -71848, 58733, -14859, -83082, -75794, -82082, -24871, -15206, 91207, -56469, -93618, 67131, -8682, 75719, 87429, -98757, -7535, -24890, -94160, 85003, 33928, 75538, 97456, -66424, -60074, -8527, -28697, -22308, 2246, -70134, -82319, -10184, 87081, -34949, -28645, -47352, -83966, -60418, -15293, -53067, -25921, 55172, 75064, 95859, 48049, 34311, -86931, -38586, 33686, -36714, 96922, 76713, -22165, -80585, -34503, -44516, 39217, -28457, 47227, -94036, 43457, 24626, -87359, 26898, -70819, 30528, -32397, -69486, 84912, -1187, -98986, -32958, 4280, -79129, -65604, 9344, 58964, 50584, 71128, -55480, 24986, 15086, -62360, -42977, -49482, -77256, -36895, -74818, 20, 3063, -49426, 28152, -97329, 6086, 86035, -88743, 35241, 44249, 19927, -10660, 89404, 24179, -26621, -6511, 57745, -28750, 96340, -97160, -97822, -49979, 52307, 79462, 94273, -24808, 77104, 9255, -83057, 77655, 21361, 55956, -9096, 48599, -40490, -55107, 2689, 29608, 20497, 66834, -34678, 23553, -81400, -66630, -96321, -34499, -12957, -20564, 25610, -4322, -58462, 20801, 53700, 71527, 24669, -54534, 57879, -3221, 33636, 3900, 97832, -27688, -98715, 5992, 24520, -55401, -57613, -69926, 57377, -77610, 20123, 52174, 860, 60429, -91994, -62403, -6218, -90610, -37263, -15052, 62069, -96465, 44254, 89892, -3406, 19121, -41842, -87783, -64125, -56120, 73904, -22797, -58118, -4866, 5356, 75318, 46119, 21276, -19246, -9241, -97425, 57333, -15802, 93149, 25689, -5532, 95716, 39209, -87672, -29470, -16324, -15331, 27632, -39454, 56530, -16000, 29853, 46475, 78242, -46602, 83192, -73440, -15816, 50964, -36601, 89758, 38375, -40007, -36675, -94030, 67576, 46811, -64919, 45595, 76530, 40398, 35845, 41791, 67697, -30439, -82944, 63115, 33447, -36046, -50122, -34789, 43003, -78947, -38763, -89210, 32756, -20389, -31358, -90526, -81607, 88741, 86643, 98422, 47389, -75189, 13091, 95993, -15501, 94260, -25584, -1483, -67261, -70753, 25160, 89614, -90620, -48542, 83889, -12388, -9642, -37043, -67663, 28794, -8801, 13621, 12241, 55379, 84290, 21692, -95906, -85617, -17341, -63767, 80183, -4942, -51478, 30997, -13658, 8838, 17452, -82869, -39897, 68449, 31964, 98158, -49489, 62283, -62209, -92792, -59342, 55146, -38533, 20496, 62667, 62593, 36095, -12470, 5453, -50451, 74716, -17902, 3302, -16760, -71642, -34819, 96459, -72860, 21638, 47342, -69897, -40180, 44466, 76496, 84659, 13848, -91600, -90887, -63742, -2156, -84981, -99280, 94326, -33854, 92029, -50811, 98711, -36459, -75555, 79110, -88164, -97397, -84217, 97457, 64387, 30513, -53190, -83215, 252, 2344, -27177, -92945, -89010, 82662, -11670, 86069, 53417, 42702, 97082, 3695, -14530, -46334, 17910, 77999, 28009, -12374, 15498, -46941, 97088, -35030, 95040, 92095, -59469, -24761, 46491, 67357, -66658, 37446, -65130, -50416, 99197, 30925, 27308, 54122, -44719, 12582, -99525, -38446, -69050, -22352, 94757, -56062, 33684, -40199, -46399, 96842, -50881, -22380, -65021, 40582, 53623, -76034, 77018, -97074, -84838, -22953, -74205, 79715, -33920, -35794, -91369, 73421, -82492, 63680, -14915, -33295, 37145, 76852, -69442, 60125, -74166, 74308, -1900, -30195, -16267, -60781, -27760, 5852, 38917, 25742, -3765, 49097, -63541, 98612, -92865, -30248, 9612, -8798, 53262, 95781, -42278, -36529, 7252, -27394, -5021, 59178, 80934, -48480, -75131, -54439, -19145, -48140, 98457, -6601, -51616, -89730, 78028, 32083, -48904, 16822, -81153, -8832, 48720, -80728, -45133, -86647, -4259, -40453, 2590, 28613, 50523, -4105, -27790, -74579, -17223, 63721, 33489, -47921, 97628, -97691, -14782, -65644, 18008, -93651, -71266, 80990, -76732, -47104, 35368, 28632, 59818, -86269, -89753, 34557, -92230, -5933, -3487, -73557, -13174, -43981, -43630, -55171, 30254, -83710, -99583, -13500, 71787, 5017, -25117, -78586, 86941, -3251, -23867, -36315, 75973, 86272, -45575, 77462, -98836, -10859, 70168, -32971, -38739, -12761, 93410, 14014, -30706, -77356, -85965, -62316, 63918, -59914, -64088, 1591, -10957, 38004, 15129, -83602, -51791, 34381, -89382, -26056, 8942, 5465, 71458, -73805, -87445, -19921, -80784, 69150, -34168, 28301, -68955, 18041, 6059, 82342, 9947, 39795, 44047, -57313, 48569, 81936, -2863, -80932, 32976, -86454, -84207, 33033, 32867, 9104, -16580, -25727, 80157, -70169, 53741, 86522, 84651, 68480, 84018, 61932, 7332, -61322, -69663, 76370, 41206, 12326, -34689, 17016, 82975, -23386, 39417, 72793, 44774, -96259, 3213, 79952, 29265, -61492, -49337, 14162, 65886, 3342, -41622, -62659, -90402, -24751, 88511, 54739, -21383, -40161, -96610, -24944, -602, -76842, -21856, 69964, 43994, -15121, -85530, 12718, 13170, -13547, 69222, 62417, -75305, -81446, -38786, -52075, -23110, 97681, -82800, -53178, 11474, 35857, 94197, -58148, -23689, 32506, 92154, -64536, -73930, -77138, 97446, -83459, 70963, 22452, 68472, -3728, -25059, -49405, 95129, -6167, 12808, 99918, 30113, -12641, -26665, 86362, -33505, 50661, 26714, 33701, 89012, -91540, 40517, -12716, -57185, -87230, 29914, -59560, 13200, -72723, 58272, 23913, -45586, -96593, -26265, -2141, 31087, 81399, 92511, -34049, 20577, 2803, 26003, 8940, 42117, 40887, -82715, 38269, 40969, -50022, 72088, 21291, -67280, -16523, 90535, 18669, 94342, -39568, -88080, -99486, -20716, 23108, -28037, 63342, 36863, -29420, -44016, 75135, 73415, 16059, -4899, 86893, 43136, -7041, 33483, -67612, 25327, 40830, 6184, 61805, 4247, 81119, -22854, -26104, -63466, 63093, -63685, 60369, 51023, 51644, -16350, 74438, -83514, 99083, 10079, -58451, -79621, 48471, 67131, -86940, 99093, 11855, -22272, -67683, -44371, 9541, 18123, 37766, -70922, 80385, -57513, -76021, -47890, 36154, 72935, 84387, -92681, -88303, -7810, 59902, -90, -64704, -28396, -66403, 8860, 13343, 33882, 85680, 7228, 28160, -14003, 54369, -58893, 92606, -63492, -10101, 64714, 58486, 29948, -44679, -22763, 10151, -56695, 4031, -18242, -36232, 86168, -14263, 9883, 47124, 47271, 92761, -24958, -73263, -79661, -69147, -18874, 29546, -92588, -85771, 26451, -86650, -43306, -59094, -47492, -34821, -91763, -47670, 33537, 22843, 67417, -759, 92159, 63075, 94065, -26988, 55276, 65903, 30414, -67129, -99508, -83092, -91493, -50426, 14349, -83216, -76090, 32742, -5306, -93310, -60750, -60620, -45484, -21108, -58341, -28048, -52803, 69735, 78906, 81649, 32565, -86804, -83202, -65688, -1760, 89707, 93322, -72750, 84134, 71900, -37720, 19450, -78018, 22001, -23604, 26276, -21498, 65892, -72117, -89834, -23867, 55817, -77963, 42518, 93123, -83916, 63260, -2243, -97108, 85442, -36775, 17984, -58810, 99664, -19082, 93075, -69329, 87061, 79713, 16296, 70996, 13483, -74582, 49900, -27669, -40562, 1209, -20572, 34660, 83193, 75579, 7344, 64925, 88361, 60969, 3114, 44611, -27445, 53049, -16085, -92851, -53306, 13859, -33532, 86622, -75666, -18159, -98256, 51875, -42251, -27977, -18080, 23772, 38160, 41779, 9147, 94175, 99905, -85755, 62535, -88412, -52038, -68171, 93255, -44684, -11242, -104, 31796, 62346, -54931, -55790, -70032, 46221, 56541, -91947, 90592, 93503, 4071, 20646, 4856, -63598, 15396, -50708, 32138, -85164, 38528, -89959, 53852, 57915, -42421, -88916, -75072, 67030, -29066, 49542, -71591, 61708, -53985, -43051, 28483, 46991, -83216, 80991, -46254, -48716, 39356, -8270, -47763, -34410, 874, -1186, -7049, 28846, 11276, 21960, -13304, -11433, -4913, 55754, 79616, 70423, -27523, 64803, 49277, 14906, -97401, -92390, 91075, 70736, 21971, -3303, 55333, -93996, 76538, 54603, -75899, 98801, 46887, 35041, 48302, -52318, 55439, 24574, 14079, -24889, 83440, 14961, 34312, -89260, -22293, -81271, -2586, -71059, -10640, -93095, -5453, -70041, 66543, 74012, -11662, -52477, -37597, -70919, 92971, -17452, -67306, -80418, 7225, -89296, 24296, 86547, 37154, -10696, 74436, -63959, 58860, 33590, -88925, -97814, -83664, 85484, -8385, -50879, 57729, -74728, -87852, -15524, -91120, 22062, 28134, 80917, 32026, 49707, -54252, -44319, -35139, 13777, 44660, 85274, 25043, 58781, -89035, -76274, 6364, -63625, 72855, 43242, -35033, 12820, -27460, 77372, -47578, -61162, -70758, -1343, -4159, 64935, 56024, -2151, 43770, 19758, -30186, -86040, 24666, -62332, -67542, 73180, -25821, -27826, -45504, -36858, -12041, 20017, -24066, -56625, -52097, -47239, -90694, 8959, 7712, -14258, -5860, 55349, 61808, -4423, -93703, 64681, -98641, -25222, 46999, -83831, -54714, 19997, -68477, 66073, 51801, -66491, 52061, -52866, 79907, -39736, -68331, 68937, 91464, 98892, 910, 93501, 31295, -85873, 27036, -57340, 50412, 21, -2445, 29471, 71317, 82093, -94823, -54458, -97410, 39560, -7628, 66452, 39701, 54029, 37906, 46773, 58296, 60370, -61090, 85501, -86874, 71443, -72702, -72047, 14848, 34102, 77975, -66294, -36576, 31349, 52493, -70833, -80287, 94435, 39745, -98291, 84524, -18942, 10236, 93448, 50846, 94023, -6939, 47999, 14740, 30165, 81048, 84935, -19177, -13594, 32289, 62628, -90612, -542, -66627, 64255, 71199, -83841, -82943, -73885, 8623, -67214, -9474, -35249, 62254, -14087, -90969, 21515, -83303, 94377, -91619, 19956, -98810, 96727, -91939, 29119, -85473, -82153, -69008, 44850, 74299, -76459, -86464, 8315, -49912, -28665, 59052, -69708, 76024, -92738, 50098, 18683, -91438, 18096, -19335, 35659, 91826, 15779, -73070, 67873, -12458, -71440, -46721, 54856, 97212, -81875, 35805, 36952, 68498, 81627, -34231, 81712, 27100, -9741, -82612, 18766, -36392, 2759, 41728, 69743, 26825, 48355, -17790, 17165, 56558, 3295, -24375, 55669, -16109, 24079, 73414, 48990, -11931, -78214, 90745, 19878, 35673, -15317, -89086, 94675, -92513, 88410, -93248, -19475, -74041, -19165, 32329, -26266, -46828, -18747, 45328, 8990, -78219, -25874, -74801, -44956, -54577, -29756, -99822, -35731, -18348, -68915, -83518, -53451, 95471, -2954, -13706, -8763, -21642, -37210, 16814, -60070, -42743, 27697, -36333, -42362, 11576, 85742, -82536, 68767, -56103, -63012, 71396, -78464, -68101, -15917, -11113, -3596, 77626, -60191, -30585, -73584, 6214, -84303, 18403, 23618, -15619, -89755, -59515, -59103, -74308, -63725, -29364, -52376, -96130, 70894, -12609, 50845, -2314, 42264, -70825, 64481, 55752, 4460, -68603, -88701, 4713, -50441, -51333, -77907, 97412, -66616, -49430, 60489, -85262, -97621, -18980, 44727, -69321, -57730, 66287, -92566, -64427, -14270, 11515, -92612, -87645, 61557, 24197, -81923, -39831, -10301, -23640, -76219, -68025, 92761, -76493, 68554, -77734, -95620, -11753, -51700, 98234, -68544, -61838, 29467, 46603, -18221, -35441, 74537, 40327, -58293, 75755, -57301, -7532, -94163, 18179, -14388, -22258, -46417, -48285, 18242, -77551, 82620, 250, -20060, -79568, -77259, 82052, -98897, -75464, 48773, -79040, -11293, 45941, -67876, -69204, -46477, -46107, 792, 60546, -34573, -12879, -94562, 20356, -48004, -62429, 96242, 40594, 2099, 99494, 25724, -39394, -2388, -18563, -56510, -83570, -29214, 3015, 74454, 74197, 76678, -46597, 60630, -76093, 37578, -82045, -24077, 62082, -87787, -74936, 58687, 12200, -98952, 70155, -77370, 21710, -84625, -60556, -84128, 925, 65474, -15741, -94619, 88377, 89334, 44749, 22002, -45750, -93081, -14600, -83447, 46691, 85040, -66447, -80085, 56308, 44310, 24979, -29694, 57991, 4675, -71273, -44508, 13615, -54710, 23552, -78253, -34637, 50497, 68706, 81543, -88408, -21405, 6001, -33834, -21570, -46692, -25344, 20310, 71258, -97680, 11721, 59977, 59247, -48949, 98955, -50276, -80844, -27935, -76102, 55858, -33492, 40680, 66691, -33188, 8284, 64893, -7528, 6019, -85523, 8434, -64366, -56663, 26862, 30008, -7611, -12179, -70076, 21426, -11261, -36864, -61937, -59677, 929, -21052, 3848, -20888, -16065, 98995, -32293, -86121, -54564, 77831, 68602, 74977, 31658, 40699, 29755, 98424, 80358, -69337, 26339, 13213, -46016, -18331, 64713, -46883, -58451, -70024, -92393, -4088, 70628, -51185, 71164, -75791, -1636, -29102, -16929, -87650, -84589, -24229, -42137, -15653, 94825, 13042, 88499, -47100, -90358, -7180, 29754, -65727, -42659, -85560, -9037, -52459, 20997, -47425, 17318, 21122, 20472, -23037, 65216, -63625, -7877, -91907, 24100, -72516, 22903, -85247, -8938, 73878, 54953, 87480, -31466, -99524, 35369, -78376, 89984, -15982, 94045, -7269, 23319, -80456, -37653, -76756, 2909, 81936, 54958, -12393, 60560, -84664, -82413, 66941, -26573, -97532, 64460, 18593, -85789, -38820, -92575, -43663, -89435, 83272, -50585, 13616, -71541, -53156, 727, -27644, 16538, 34049, 57745, 34348, 35009, 16634, -18791, 23271, -63844, 95817, 21781, 16590, 59669, 15966, -6864, 48050, -36143, 97427, -59390, 96931, 78939, -1958, 50777, 43338, -51149, 39235, -27054, -43492, 67457, -83616, 37179, 10390, 85818, 2391, 73635, 87579, -49127, -81264, -79023, -81590, 53554, -74972, -83940, -13726, -39095, 29174, 78072, 76104, 47778, 25797, -29515, -6493, -92793, 22481, -36197, -65560, 42342, 15750, 97556, 99634, -56048, -35688, 13501, 63969, -74291, 50911, 39225, 93702, -3490, -59461, -30105, -46761, -80113, 92906, -68487, 50742, 36152, -90240, -83631, 24597, -50566, -15477, 18470, 77038, 40223, -80364, -98676, 70957, -63647, 99537, 13041, 31679, 86631, 37633, -16866, 13686, -71565, 21652, -46053, -80578, -61382, 68487, -6417, 4656, 20811, 67013, -30868, -11219, 46, 74944, 14627, 56965, 42275, -52480, 52162, -84883, -52579, -90331, 92792, 42184, -73422, -58440, 65308, -25069, 5475, -57996, 59557, -17561, 2826, -56939, 14996, -94855, -53707, 99159, 43645, -67719, -1331, 21412, 41704, 31612, 32622, 1919, -69333, -69828, 22422, -78842, 57896, -17363, 27979, -76897, 35008, 46482, -75289, 65799, 20057, 7170, 41326, -76069, 90840, -81253, -50749, 3649, -42315, 45238, -33924, 62101, 96906, 58884, -7617, -28689, -66578, 62458, 50876, -57553, 6739, 41014, -64040, -34916, 37940, 13048, -97478, -11318, -89440, -31933, -40357, -59737, -76718, -14104, -31774, 28001, 4103, 41702, -25120, -31654, 63085, -3642, 84870, -83896, -76422, -61520, 12900, 88678, 85547, 33132, -88627, 52820, 63915, -27472, 78867, -51439, 33005, -23447, -3271, -39308, 39726, -74260, -31874, -36893, 93656, 910, -98362, 60450, -88048, 99308, 13947, 83996, -90415, -35117, 70858, -55332, -31721, 97528, 82982, -86218, 6822, 25227, 36946, 97077, -4257, -41526, 56795, 89870, 75860, -70802, 21779, 14184, -16511, -89156, -31422, 71470, 69600, -78498, 74079, -19410, 40311, 28501, 26397, -67574, -32518, 68510, 38615, 19355, -6088, -97159, -29255, -92523, 3023, -42536, -88681, 64255, 41206, 44119, 52208, 39522, -52108, 91276, -70514, 83436, 63289, -79741, 9623, 99559, 12642, 85950, 83735, -21156, -67208, 98088, -7341, -27763, -30048, -44099, -14866, -45504, -91704, 19369, 13700, 10481, -49344, -85686, 33994, 19672, 36028, 60842, 66564, -24919, 33950, -93616, -47430, -35391, -28279, 56806, 74690, 39284, -96683, -7642, -75232, 37657, -14531, -86870, -9274, -26173, 98640, 88652, 64257, 46457, 37814, -19370, 9337, -22556, -41525, 39105, -28719, 51611, -93252, 98044, -90996, 21710, -47605, -64259, -32727, 53611, -31918, -3555, 33316, -66472, 21274, -37731, -2919, 15016, 48779, -88868, 1897, 41728, 46344, -89667, 37848, 68092, -44011, 85354, -43776, 38739, -31423, -66330, 65167, -22016, 59405, 34328, -60042, 87660, -67698, -59174, -1408, -46809, -43485, -88807, -60489, 13974, 22319, 55836, -62995, -37375, -4185, 32687, -36551, -75237, 58280, 26942, -73756, 71756, 78775, -40573, 14367, -71622, -77338, 24112, 23414, -7679, -51721, 87492, 85066, -21612, 57045, 10673, -96836, 52461, -62218, -9310, 65862, -22748, 89906, -96987, -98698, 26956, -43428, 46141, 47456, 28095, 55952, 67323, -36455, -60202, -43302, -82932, 42020, 77036, 10142, 60406, 70331, 63836, 58850, -66752, 52109, 21395, -10238, -98647, -41962, 27778, 69060, 98535, -28680, -52263, -56679, 66103, -42426, 27203, 80021, 10153, 58678, 36398, 63112, 34911, 20515, 62082, -15659, -40785, 27054, 43767, -20289, 65838, -6954, -60228, -72226, 52236, -35464, 25209, -15462, -79617, -41668, -84083, 62404, -69062, 18913, 46545, 20757, 13805, 24717, -18461, -47009, -25779, 68834, 64824, 34473, 39576, 31570, 14861, -15114, -41233, 95509, 68232, 67846, 84902, -83060, 17642, -18422, 73688, 77671, -26930, 64484, -99637, 73875, 6428, 21034, -73471, 19664, -68031, 15922, -27028, 48137, 54955, -82793, -41144, -10218, -24921, -28299, -2288, 68518, -54452, 15686, -41814, 66165, -72207, -61986, 80020, 50544, -99500, 16244, 78998, 40989, 14525, -56061, -24692, -94790, 21111, 37296, -90794, 72100, 70550, -31757, 17708, -74290, 61910, 78039, -78629, -25033, 73172, -91953, 10052, 64502, 99585, -1741, 90324, -73723, 68942, 28149, 30218, 24422, 16659, 10710, -62594, 94249, 96588, 46192, 34251, 73500, -65995, -81168, 41412, -98724, -63710, -54696, -52407, 19746, 45869, 27821, -94866, -76705, -13417, -61995, -71560, 43450, 67384, -8838, -80293, -28937, 23330, -89694, -40586, 46918, 80429, -5475, 78013, 25309, -34162, 37236, -77577, 86744, 26281, -29033, -91813, 35347, 13033, -13631, -24459, 3325, -71078, -75359, 81311, 19700, 47678, -74680, -84113, 45192, 35502, 37675, 19553, 76522, -51098, -18211, 89717, 4508, -82946, 27749, 85995, 89912, -53678, -64727, -14778, 32075, -63412, -40524, 86440, -2707, -36821, 63850, -30883, 67294, -99468, -23708, 34932, 34386, 98899, 29239, -23385, 5897, 54882, 98660, 49098, 70275, 17718, 88533, 52161, 63340, 50061, -89457, 19491, -99156, 24873, -17008, 64610, -55543, 50495, 17056, -10400, -56678, -29073, -42960, -76418, 98562, -88104, -96255, 10159, -90724, 54011, 12052, 45871, -90933, -69420, 67039, 37202, 78051, -52197, -40278, -58425, 65414, -23394, -1415, 6912, -53447, 7352, 17307, -78147, 63727, 98905, 55412, -57658, -32884, -44878, 22755, 39730, 3638, 35111, 39777, 74193, 38736, -11829, -61188, -92757, 55946, -71232, -63032, -83947, 39147, -96684, -99233, 25131, -32197, 24406, -55428, -61941, 25874, -69453, 64483, -19644, -68441, 12783, 87338, -48676, 66451, -447, -61590, 50932, -11270, 29035, 65698, -63544, 10029, 80499, -9461, 86368, 91365, -81810, -71914, -52056, -13782, 44240, -30093, -2437, 24007, 67581, -17365, -69164, -8420, -69289, -29370, 48010, 90439, 13141, 69243, 50668, 39328, 61731, 78266, -81313, 17921, -38196, 55261, 9948, -24970, 75712, -72106, 28696, 7461, 31621, 61047, 51476, 56512, 11839, -96916, -82739, 28924, -99927, 58449, 37280, 69357, 11219, -32119, -62050, -48745, -83486, -52376, 42668, 82659, 68882, 38773, 46269, -96005, 97630, 25009, -2951, -67811, 99801, 81587, -79793, -18547, -83086, 69512, 33127, -92145, -88497, 47703, 59527, 1909, 88785, -88882, 69188, -46131, -5589, -15086, 36255, -53238, -33009, 82664, 53901, 35939, -42946, -25571, 33298, 69291, 53199, 74746, -40127, -39050, 91033, 51717, -98048, 87240, 36172, 65453, -94425, -63694, -30027, 59004, 88660, 3649, -20267, -52565, -67321, 34037, 4320, 91515, -56753, 60115, 27134, 68617, -61395, -26503, -98929, -8849, -63318, 10709, -16151, 61905, -95785, 5262, 23670, -25277, 90206, -19391, 45735, 37208, -31992, -92450, 18516, -90452, -58870, -58602, 93383, 14333, 17994, 82411, -54126, -32576, 35440, -60526, -78764, -25069, -9022, -394, 92186, -38057, 55328, -61569, 67780, 77169, 19546, -92664, -94948, 44484, -13439, 83529, 27518, -48333, 72998, 38342, -90553, -98578, -76906, 81515, -16464, 78439, 92529, 35225, -39968, -10130, -7845, -32245, -74955, -74996, 67731, -13897, -82493, 33407, 93619, 59560, -24404, -57553, 19486, -45341, 34098, -24978, -33612, 79058, 71847, 76713, -95422, 6421, -96075, -59130, -28976, -16922, -62203, 69970, 68331, 21874, 40551, 89650, 51908, 58181, 66480, -68177, 34323, -3046, -49656, -59758, 43564, -10960, -30796, 15473, -20216, 46085, -85355, 41515, -30669, -87498, 57711, 56067, 63199, -83805, 62042, 91213, -14606, 4394, -562, 74913, 10406, 96810, -61595, 32564, 31640, -9732, 42058, 98052, -7908, -72330, 1558, -80301, 34878, 32900, 3939, -8824, 88316, 20937, 21566, -3218, -66080, -31620, 86859, 54289, 90476, -42889, -15016, -18838, 75456, 30159, -67101, 42328, -92703, 85850, -5475, 23470, -80806, 68206, 17764, 88235, 46421, -41578, 74005, -81142, 80545, 20868, -1560, 64017, 83784, 68863, -97516, -13016, -72223, 79630, -55692, 82255, 88467, 28007, -34686, -69049, -41677, 88535, -8217, 68060, -51280, 28971, 49088, 49235, 26905, -81117, -44888, 40623, 74337, -24662, 97476, 79542, -72082, -35093, 98175, -61761, -68169, 59697, -62542, -72965, 59883, -64026, -37656, -92392, -12113, -73495, 98258, 68379, -21545, 64607, -70957, -92254, -97460, -63436, -8853, -19357, -51965, -76582, 12687, -49712, 45413, -60043, 33496, 31539, -57347, 41837, 67280, -68813, 52088, -13155, -86430, -15239, -45030, 96041, 18749, -23992, 46048, 35243, -79450, 85425, -58524, 88781, -39454, 53073, -48864, -82289, 39086, 82540, -11555, 25014, -5431, -39585, -89526, 2705, 31953, -81611, 36985, -56022, 68684, -27101, 11422, 64655, -26965, -63081, -13840, -91003, -78147, -8966, 41488, 1988, 99021, -61575, -47060, 65260, -23844, -21781, -91865, -19607, 44808, 2890, 63692, -88663, -58272, 15970, -65195, -45416, -48444, -78226, -65332, -24568, 42833, -1806, -71595, 80002, -52250, 30952, 48452, -90106, 31015, -22073, 62339, 63318, 78391, 28699, 77900, -4026, -76870, -45943, 33665, 9174, -84360, -22684, -16832, -67949, -38077, -38987, -32847, 51443, -53580, -13505, 9344, -92337, 26585, 70458, -52764, -67471, -68411, -1119, -2072, -93476, 67981, 40887, -89304, -12235, 41488, 1454, 5355, -34855, -72080, 24514, -58305, 3340, 34331, 8731, 77451, -64983, -57876, 82874, 62481, -32754, -39902, 22451, -79095, -23904, 78409, -7418, 77916}; + //List> resultList = threeSum(nums); //List> resultList = threeSum2(nums); - + List> resultList = threeSum3(nums); for (List list : resultList) { for (Integer num : list) { System.out.print(num + " "); @@ -93,6 +93,37 @@ private static List> threeSum2(int[] nums) { return resultList; } + + private static List> threeSum3(int[] nums) { + List> result = new ArrayList<>(); + + if (nums == null || nums.length == 0) { + return result; + } + + // 对数组进行排序, 确保可以使用第2和第3个元素前后双指针 + Arrays.sort(nums); + + // 遍历第一个元素 + for (int i = 0; i < nums.length - 2; i++) { + // 第2个元素从第一个的下一个开始 + int left = i + 1; + // 第3个元素从后面开始 + int right = nums.length - 1; + while (left < right) { + // 符合条件的 + if (nums[left] + nums[right] == -nums[i]) { + result.add(Arrays.asList(nums[i], nums[left], nums[right])); + left++; //之后将left++, 再找满足条件的right + } else { + right--;//如果right过大不满足,那就往前挪动变小一些 + } + } + } + return result; + } + + /** * 判断resultList是否有一条集合包含a,b,c3个元素 * diff --git a/src/main/java/com/study/search/BFS.java b/src/main/java/com/study/search/BFS.java index b29041f..114768a 100644 --- a/src/main/java/com/study/search/BFS.java +++ b/src/main/java/com/study/search/BFS.java @@ -9,10 +9,13 @@ * BFS 广度优先搜索 * 按层级一层一层进行搜索 * + * 常见的解法有: 循环 + 队列 + * * https://leetcode-cn.com/problems/binary-tree-level-order-traversal/ */ public class BFS { + public static void main(String[] args) { int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; List treeNodes = TreeUtils.buildTree(arr); diff --git a/src/main/java/com/study/search/BinarySearch.java b/src/main/java/com/study/search/BinarySearch.java index 66a5f17..dfaa357 100644 --- a/src/main/java/com/study/search/BinarySearch.java +++ b/src/main/java/com/study/search/BinarySearch.java @@ -14,7 +14,8 @@ public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8}; //int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}; - System.out.println(search(arr, 1)); + //System.out.println(search(arr, 1)); + System.out.println(search2(arr, 3)); } /** @@ -36,7 +37,7 @@ private static int search(int[] arr, int key) { // 如果小于中间的,就把范围改成最左边到中间-1 if (key < arr[mid]) { high = mid - 1; - // 如果大于中间的,就把范围改成中间+1到最右边 + // 如果大于中间的,就把范围改成中间+1到最右边 } else if (key > arr[mid]) { low = mid + 1; } else { @@ -47,4 +48,33 @@ private static int search(int[] arr, int key) { //没找到返回-1 return -1; } + + + /** + * 折半查找,每次查找都基于中间值来对比 来缩小查找范围, 时间复杂度为O1 + * + * @param arr + * @param element + * @return + */ + private static int search2(int[] arr, int element) { + int low = 0, high = arr.length - 1; + + // 循环条件是最低位索引小于等于最高位索引 + while (low <= high) { + int mid = (low + high) / 2; + // 如果元素大于中间值, 则下次从右边查找 + if (arr[mid] < element) { + // 将最小索引改为中间+! + low = mid + 1; + } else if (arr[mid] > element) { + // 将最大索引改为中间-1 + high = mid - 1; + } else { + return mid; + } + } + + return -1; + } } diff --git a/src/main/java/com/study/sort/QuickSort.java b/src/main/java/com/study/sort/QuickSort.java index e1097f4..d0e86a3 100644 --- a/src/main/java/com/study/sort/QuickSort.java +++ b/src/main/java/com/study/sort/QuickSort.java @@ -2,29 +2,32 @@ /** * 快速排序算法 - * + *

* 最简单的理解: 确保数组里面每一个元素当做基准值后,前后的元素都比它小或者大. 即完成排序. - * - * + *

+ *

* 通过一轮的排序将序列分割成独立的两部分,其中一部分序列的关键字(这里主要用值来表示)均比另一部分关键字小。 * 继续对长度较短的序列进行同样的分割,最后到达整体有序。在排序过程中,由于已经分开的两部分的元素不需要进行比较,故减少了比较次数,降低了排序时间。 - * + *

* 快速排序在进行交换时,只是根据比较基数值判断是否交换,且不是相邻元素来交换,在交换过程中可能改变相同元素的顺序,因此是一种不稳定的排序算法。 - * + *

* 时间复杂度介于O(nlogn) 和O(n^2) 之间 - * + * https://www.cnblogs.com/yueansixing/articles/9125634.html + *

* https://www.cnblogs.com/foreverking/articles/2234225.html */ public class QuickSort { public static void main(String[] args) { - int[] a = {12, 20, 5, 16, 15, 1, 30, 45}; - // int[] a = {12, 20, 9, 5, 16}; + //int[] a = {12, 20, 5, 16, 15, 1, 30, 45}; + int[] a = {21, 32, 43, 98, 54, 45, 23, 4, 66, 86}; + // int[] a = {12, 20, 9, 5, 16}; //int[] a = {12, 1}; + print(a); int start = 0; int end = a.length - 1; - //sort(a, start, end); - sort2(a, start, end); + sort(a, start, end); + //sort2(a, start, end); for (int i = 0; i < a.length; i++) { System.out.printf("%s ", a[i]); } @@ -51,12 +54,12 @@ private static void sort(int[] a, int low, int high) { int end = high; // 选取第一个元素作为基准值 int key = a[low]; - + System.out.println("基准值为: "+ key); // 循环里 start往后挪, end往前挪 while (end > start) { // 从后往前比较 // 如果没有比基准值小的,比较下一个,直到有比基准值小的交换位置,将此时start和end上的元素进行交换;然后又从前往后比较, - while (end > start && a[end] >= key){ + while (end > start && a[end] >= key) { end--; } // 把小于基准值的放到基准值左边 @@ -64,10 +67,12 @@ private static void sort(int[] a, int low, int high) { int temp = a[end]; a[end] = a[start]; a[start] = temp; + System.out.println(String.format("从后往前: 将%d和%d进行对调", a[end], a[start])); + print(a); } // 从前往后比较 // 如果没有比基准值大的,交换的为start和end索引上的元素,比较下一个,直到有比基准值大的交换位置,之后继续循环,又从后往前比较 - while (end > start && a[start] <= key){ + while (end > start && a[start] <= key) { start++; } // 把大于基准值的放到基准值右边 @@ -75,65 +80,78 @@ private static void sort(int[] a, int low, int high) { int temp = a[start]; a[start] = a[end]; a[end] = temp; + System.out.println(String.format("从前往后: 将%d和%d进行对调", a[start], a[end])); + print(a); } } - // 当循环结束后,基准值的位置已经确定了。左边的值都比基准值小,右边的值都比基准值大,但是两边的顺序还有可能是不一样的,进行下面的递归调用 + // 当循环结束后,基准值的位置已经确定了。左边的值都比基准值小,右边的值都比基准值大,但是两边的顺序还有可能是不一样的. - //左边序列. 第一个索引位置到基准值索引-1 + // 于是将start和end左右两边划分成两个区间, 分别进行递归排序, 等左右两个区间都完成排序后,那么整个数组就变成有序了 + // 将左边序列进行排序 if (start > low) { sort(a, low, start - 1); + System.out.println("start: " + start); } - //右边序列. 从基准值索引+1到最后一个 + // 将右边序列进行排序 if (end < high) { sort(a, end + 1, high); + System.out.println("end: " + end); } } - private static void sort2(int[] arr, int low , int high){ + private static void sort2(int[] arr, int low, int high) { int start = low; int end = high; // 取第0个元素为基准值 - int key = arr[start]; + int key = arr[low]; - while(start < end){ + while (start < end) { // 后面的比基准值大的 - while(start < end && arr[end] >= key){ - end --; //往前挪 + while (start < end && arr[end] >= key) { + end--; //往前挪 } // 直到遇上比基准值小的, 前后替换 - if(arr[end] < key){ + if (arr[end] < key) { int tmp = arr[end]; - arr[end] = arr[start]; + arr[end] = arr[start]; arr[start] = tmp; + System.out.println("后面比基准值小"); + print(arr); } // 前面比基准值小的 - while(start < end && arr[start] <= key){ - start ++; //往后挪 + while (start < end && arr[start] <= key) { + start++; //往后挪 } - if(arr[start] > key){ + if (arr[start] > key) { int tmp = arr[start]; arr[start] = arr[end]; arr[end] = tmp; + System.out.println("前面比基准值大"); + print(arr); } } - // 循环结束后, 基准值前面的都比它小, 后面的都比它大. 但顺序还不是有序的 - - // 左边, 第一个索引位置到基准值-1 - if(start > low){ + if (start > low) { System.out.println("low" + low); sort2(arr, low, start - 1); } - // 右边, 基准索引+1到最后一个 - if(end < high){ + + if (end < high) { System.out.println("high" + high); - sort2(arr, end+1 , high); + sort2(arr, end + 1, high); } } + + private static void print(int[] num) { + for (int i = 0; i < num.length; i++) { + System.out.print(num[i] + " "); + } + System.out.println(); + } } diff --git a/src/main/java/com/study/string/GenerateParentheses.java b/src/main/java/com/study/string/GenerateParentheses.java index ffad638..49b3915 100644 --- a/src/main/java/com/study/string/GenerateParentheses.java +++ b/src/main/java/com/study/string/GenerateParentheses.java @@ -30,7 +30,8 @@ public class GenerateParentheses { * @param args */ public static void main(String[] args) { - List resultList = generateParenthesis(3); + //List resultList = generateParenthesis(3); + List resultList = generateParenthesis2(3); for (String result : resultList) { System.out.println(result); @@ -46,6 +47,36 @@ private static List generateParenthesis(int n) { return resultList; } + /** + * 闭合数 + * 思路 + * + * 为了枚举某些内容,我们通常希望将其表示为更容易计算的不相交子集的总和。 + * + * 考虑有效括号序列 S 的 闭包数:至少存在 index >= 0,使得 S[0], S[1], ..., S[2*index+1]是有效的。 显然,每个括号序列都有一个唯一的闭包号。 我们可以尝试单独列举它们。 + * + * 算法 + * + * 对于每个闭合数 c,我们知道起始和结束括号必定位于索引 0 和 2*c + 1。然后两者间的 2*c 个元素一定是有效序列,其余元素一定是有效序列。 + * + * https://leetcode-cn.com/problems/generate-parentheses/solution/gua-hao-sheng-cheng-by-leetcode/ + * + * @param n + * @return + */ + private static List generateParenthesis2(int n) { + List ans = new ArrayList(); + if (n == 0) { + ans.add(""); + } else { + for (int c = 0; c < n; ++c) + for (String left: generateParenthesis2(c)) + for (String right: generateParenthesis2(n-1-c)) + ans.add("(" + left + ")" + right); + } + return ans; + } + /** * 使用递归枚举所有左右组合的情况, 增加一些限定条件(必须先有左括号再有右括号, 而且过程中, 右括号数量不能大于左括号) diff --git a/src/main/java/com/study/string/LongestPalindrome.java b/src/main/java/com/study/string/LongestPalindrome.java index f0b337b..790ef09 100644 --- a/src/main/java/com/study/string/LongestPalindrome.java +++ b/src/main/java/com/study/string/LongestPalindrome.java @@ -5,8 +5,8 @@ import java.util.Map; /** - * 计算最长回文串的字符个数 - * + * 计算最长回文串的字符串以及字符个数 + *

* 回文串是一个正读和反读都一样的字符串。对一个左边的字符 i 右边一定会有一个对称 i。比如 'abcba', 'aa','bb' 这几个回文串。其中第一个有点特殊,中间的 c 是唯一的。 * 如果让你来造一个回文串你会怎么造? 首先让它左右两边绝对对称,如果可能的话再加上一个唯一的中心。 *

@@ -29,6 +29,8 @@ public static void main(String[] args) { LongestPalindrome lp = new LongestPalindrome(); //System.out.println(lp.longestPalindrome(s)); System.out.println(lp.count(s)); + System.out.println("最长回文字符串为: " + lp.getLongestPalindrome(s)); + } public int longestPalindrome(String s) { @@ -72,4 +74,41 @@ public int count(String s) { } return count; } + + /** + * 获取最长会问字符串, 没有经过排序处理, 只是得到有效的字符 + * + * @param s + * @return + */ + public String getLongestPalindrome(String s) { + StringBuilder result = new StringBuilder(); + // 记录每个char出现的次数 + Map kv = new HashMap<>(); + for (char c : s.toCharArray()) { + if (!kv.containsKey(c)) + kv.put(c, 1); + else + kv.put(c, kv.get(c) + 1); + } + + // 计算回文数总个数 + int count = 0; + for (Map.Entry entry : kv.entrySet()) { + // 统计元素出现的总个数, 必须是出现个数>=2以上的才满足, 而且取其最大偶数次数, 比如2次的count=2, 3次的count=2,5次的count=4 + int curCount = entry.getValue() / 2 * 2; + count += curCount; + if (curCount >= 2) { + for (int i = 0; i < curCount; i++) { + result.append(entry.getKey()); + } + } + // 只有之前出现的总个数为偶数, 才允许将出现一次的元素加到总数 + if (count % 2 == 0 && entry.getValue() % 2 == 1) { + result.insert(count / 2, entry.getKey()); + count++; + } + } + return result.toString(); + } } diff --git a/src/main/java/com/study/string/ValidAnagram.java b/src/main/java/com/study/string/ValidAnagram.java index 348b2f2..e653b11 100644 --- a/src/main/java/com/study/string/ValidAnagram.java +++ b/src/main/java/com/study/string/ValidAnagram.java @@ -36,18 +36,21 @@ public static void main(String[] args) { String s2 = "tac"; ValidAnagram va = new ValidAnagram(); //System.out.printf("%s==%s: %s", s1, s2, va.isAnagram(s1, s2)); - System.out.printf("%s==%s: %s", s1, s2, va.isAnagram2(s1, s2)); + //System.out.printf("%s==%s: %s", s1, s2, va.isAnagram2(s1, s2)); + System.out.printf("%s==%s: %s", s1, s2, va.isAnagram3(s1, s2)); System.out.println(); s1 = "aaab"; s2 = "bbaa"; //System.out.printf("%s==%s: %s", s1, s2, va.isAnagram(s1, s2)); - System.out.printf("%s==%s: %s", s1, s2, va.isAnagram2(s1, s2)); + //System.out.printf("%s==%s: %s", s1, s2, va.isAnagram2(s1, s2)); + System.out.printf("%s==%s: %s", s1, s2, va.isAnagram3(s1, s2)); System.out.println(); s1 = "anagram"; s2 = "nagaram"; - System.out.printf("%s==%s: %s", s1, s2, va.isAnagram2(s1, s2)); + //System.out.printf("%s==%s: %s", s1, s2, va.isAnagram2(s1, s2)); + System.out.printf("%s==%s: %s", s1, s2, va.isAnagram3(s1, s2)); System.out.println(); } @@ -126,8 +129,33 @@ public boolean isAnagram2(String s, String t) { return true; } -// -// public boolean isAnagram2(String s, String t) { -// -// } + /** + * 将字符串的每个字符转成hash值作为key放到一个数组中进行计数, 一个字符串进行计数,另一个字符串进行减数. + * 最终如果这个数组里面所有key的值都为0, 就说明两个字符串里的字符相同 时间复杂度为O(n) + * + * @param s + * @param t + * @return + */ + public boolean isAnagram3(String s, String t) { + if (s.length() != t.length()) { + return false; + } + // 由于字符串中所有字符都为26个小写字母, 且他们对应的hashcode在97-123之间, 比如a为97,b为98... + // 将字符串s中的每个字母 减去'a' 最终得到的数字在0-26之间 + int[] counter = new int[26]; + for (int i = 0; i < s.length(); i++) { + // charAt()方法返回每个字符的hash值 + counter[s.charAt(i) - 'a']++; // 将字符串s中的每个char 减去'a', 得到的数字作为key,然后value+1 + counter[t.charAt(i) - 'a']--; // 将字符串t中的每个char 减去'a', 得到的数字作为key,然后value-1; + } + + // 如果字符串s和t的每个char都相同, 那么上面的循环结束后,counter里面每个元素的value都为0 + for (int count : counter) { + if (count != 0) { + return false; + } + } + return true; + } } diff --git a/src/main/java/com/study/trietree/Trie.java b/src/main/java/com/study/trietree/Trie.java index 09be120..8b508fc 100644 --- a/src/main/java/com/study/trietree/Trie.java +++ b/src/main/java/com/study/trietree/Trie.java @@ -1,10 +1,12 @@ package com.study.trietree; /** + * 字典树 也叫 trie树 + *

* 基本性质 - * 根节点不包含字符,除根节点外的每一个子节点都包含一个字符 - * 从根节点到某一节点。路径上经过的字符连接起来,就是该节点对应的字符串 - * 每个节点的所有子节点包含的字符都不相同 + * 1.根节点不包含字符,除根节点外的每一个子节点都包含一个字符 + * 2.从根节点到某一节点。路径上经过的字符连接起来,就是该节点对应的字符串 + * 3.每个节点的所有子节点包含的字符都不相同 *

* 应用场景 * 典型应用是用于统计,排序和保存大量的字符串(不仅限于字符串),经常被搜索引擎系统用于文本词频统计。 @@ -13,14 +15,16 @@ * 利用字符串的公共前缀来减少查询时间,最大限度的减少无谓的字符串比较,查询效率比哈希树高。 *

* https://www.cnblogs.com/xujian2014/p/5614724.html + *

+ * https://leetcode-cn.com/problems/implement-trie-prefix-tree/ */ public class Trie { public static void main(String[] args) { TrieNode root = new TrieNode(); -// String[] strs = {"banana", "band", "bee", "absolute", "acm",}; -// String[] prefix = {"ba", "b", "band", "abc",}; - String[] strs = {"北京大学", "北京科技大学", "南京北京路", "南京大学", "南京金陵十二拆",}; - String[] prefix = {"北", "北京", "北京大学", "南京", "上海浦东新区",}; + String[] strs = {"banana", "band", "bee", "absolute", "acm",}; + String[] prefix = {"ba", "b", "band", "abc",}; +// String[] strs = {"北京大学", "北京科技大学", "南京北京路", "南京大学", "南京金陵十二拆",}; +// String[] prefix = {"北", "北京", "北京大学", "南京", "上海浦东新区",}; for (String str : strs) { insert(str, root); } @@ -47,14 +51,12 @@ public static void insert(String str, TrieNode root) { TrieNode node = root; char[] letters = str.toCharArray();//将目标单词转换为char数组 for (int i = 0, len = str.length(); i < len; i++) { - //计算每个char的位置 + //计算每个char的位置(哈希值), 这样在当前层级查找该元素或节点的的时间复杂度为O(1) int pos = letters[i] - 'a'; - if (node.son[pos] == null) //如果当前节点的儿子节点中没有该字符,则构建一个TrieNode并复值该字符 - { + if (node.son[pos] == null) { //如果当前节点的儿子节点中没有该字符,则构建一个TrieNode并复值该字符 node.son[pos] = new TrieNode(); node.son[pos].val = letters[i]; - } else //如果已经存在,则将由根至该儿子节点组成的字符串模式出现的次数+1 - { + } else { //如果已经存在,则将由根至该儿子节点组成的字符串模式出现的次数+1 node.son[pos].num++; } node = node.son[pos]; diff --git a/src/main/java/com/study/trietree/TrieNode.java b/src/main/java/com/study/trietree/TrieNode.java index ee42539..3823a88 100644 --- a/src/main/java/com/study/trietree/TrieNode.java +++ b/src/main/java/com/study/trietree/TrieNode.java @@ -8,21 +8,20 @@ public class TrieNode /** * 中文 */ - private int SIZE = 65536; + //private int SIZE = 65536; /** * 英文 一共26个字母 */ - //private int SIZE = 26; + private int SIZE = 256; public int num;// 有多少单词通过这个节点,即由根至该节点组成的字符串模式出现的次数 - public TrieNode[] son;// 所有的儿子节点 + public TrieNode[] son = new TrieNode[SIZE];// 所有的儿子节点 public boolean isEnd;// 是不是最后一个节点 public char val;// 节点的值 public TrieNode() { num = 1; - son = new TrieNode[SIZE]; isEnd = false; } } \ No newline at end of file From e2e766ab62cc60d4f4fd51e5c9a493c411bee7b1 Mon Sep 17 00:00:00 2001 From: longwu Date: Fri, 31 Jan 2020 22:26:43 +0800 Subject: [PATCH 43/74] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AD=97=E5=85=B8?= =?UTF-8?q?=E6=A0=91=E5=8D=95=E8=AF=8D=E6=89=93=E5=8D=B0=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/study/trietree/Trie.java | 84 ++++++++++++++++--- .../java/com/study/trietree/TrieNode.java | 2 +- 2 files changed, 73 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/study/trietree/Trie.java b/src/main/java/com/study/trietree/Trie.java index 8b508fc..3370b3c 100644 --- a/src/main/java/com/study/trietree/Trie.java +++ b/src/main/java/com/study/trietree/Trie.java @@ -13,6 +13,9 @@ *

* 优点 * 利用字符串的公共前缀来减少查询时间,最大限度的减少无谓的字符串比较,查询效率比哈希树高。 + * + * 遍历算法: bfs深度优先遍历 先根再子节点 + * *

* https://www.cnblogs.com/xujian2014/p/5614724.html *

@@ -28,11 +31,25 @@ public static void main(String[] args) { for (String str : strs) { insert(str, root); } - //System.out.println(tree.has("abc")); - System.out.println(has("北京", root)); - preTraverse(root); + + System.out.println("------打印字典树中所有单词--------"); + printAllWords(root , "#"); + System.out.println("--------------------------------"); + + // 是否包含这个单词 + System.out.println("------判断是否有ba这个单词--------"); + System.out.println(containsWord("ba", root)); + //System.out.println(containsWord("北京", root)); + System.out.println("--------------------------------"); + + // 打印包含该前缀的单词 + System.out.println("-------打印所有包含ba前缀的单词----"); + System.out.println(hasPrefix("ba", root)); + System.out.println("--------------------------------"); + + //preTraverse(root); + preTraverse2(root); System.out.println(); - //tree.printAllWords(); for (String pre : prefix) { int num = countPrefix(pre, root); System.out.println(pre + " 数量:" + num); @@ -112,7 +129,7 @@ public static String hasPrefix(String prefix, TrieNode root) { } /** - * 遍历经过此节点的单词. + * 打印经过此节点的所有单词. * * @param root * @param prefix @@ -135,7 +152,7 @@ public static void preTraverse(TrieNode root, String prefix) { * @param str * @return */ - public static boolean has(String str, TrieNode root) { + public static boolean containsWord(String str, TrieNode root) { if (str == null || str.length() == 0) { return false; } @@ -154,18 +171,61 @@ public static boolean has(String str, TrieNode root) { } /** - * 前序遍历字典树. + * 打印字典树里面所有字符char + * + * 前序遍历字典树. bfs深度优先遍历 + * *

- * 根 - 左 - 右 + * 根 - 所有子节点 * * @param root */ public static void preTraverse(TrieNode root) { - if (root != null) { - System.out.print(root.val + "-"); - for (TrieNode child : root.son) { - preTraverse(child); + if (root == null) { + return; + } + System.out.print(root.val + ":" + root.num + " "); + for (TrieNode child : root.son) { + preTraverse(child); + } + } + + /** + * 打印字典树里面所有字符char + * + * @param root + */ + public static void preTraverse2(TrieNode root) { + if (root == null) { + return; + } + for (TrieNode child : root.son) { + if (child != null) { + System.out.print(child.val + ":" + child.num + " "); + if (child.isEnd) + System.out.println(); } + preTraverse2(child); + } + } + + /** + * 打印字典树里所有的单词 + * 将递归走的每个char进行拼接,直到单词尾部,然后输出这个单词 + * + * @param root 根节点 + * @param prefix 前缀 + */ + private static void printAllWords(TrieNode root, String prefix) { + if (root != null && root.isEnd) { + System.out.println(prefix); + return; + } + if (root == null) + return; + for (TrieNode child : root.son) { + if (child != null) + printAllWords(child, prefix + child.val); } } } \ No newline at end of file diff --git a/src/main/java/com/study/trietree/TrieNode.java b/src/main/java/com/study/trietree/TrieNode.java index 3823a88..391e459 100644 --- a/src/main/java/com/study/trietree/TrieNode.java +++ b/src/main/java/com/study/trietree/TrieNode.java @@ -18,7 +18,7 @@ public class TrieNode public int num;// 有多少单词通过这个节点,即由根至该节点组成的字符串模式出现的次数 public TrieNode[] son = new TrieNode[SIZE];// 所有的儿子节点 public boolean isEnd;// 是不是最后一个节点 - public char val;// 节点的值 + public char val = '#';// 节点的值 public TrieNode() { num = 1; From 0078a0decab8c463fb9edcefbc94fcf1f4f47f7c Mon Sep 17 00:00:00 2001 From: longwu Date: Sun, 2 Feb 2020 17:12:01 +0800 Subject: [PATCH 44/74] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=B7=B1=E5=BA=A6?= =?UTF-8?q?=E4=BC=98=E5=85=88=E6=90=9C=E7=B4=A2=E8=A7=A3=E5=86=B34?= =?UTF-8?q?=E7=9A=87=E5=90=8E=E5=92=8CN=E7=9A=87=E5=90=8E=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/study/traceback/Queen4.java | 127 ++++++++++++++++++ .../java/com/study/traceback/Queen4_2.java | 85 ++++++++++++ src/main/java/com/study/traceback/QueenN.java | 75 +++++++++++ 3 files changed, 287 insertions(+) create mode 100644 src/main/java/com/study/traceback/Queen4.java create mode 100644 src/main/java/com/study/traceback/Queen4_2.java create mode 100644 src/main/java/com/study/traceback/QueenN.java diff --git a/src/main/java/com/study/traceback/Queen4.java b/src/main/java/com/study/traceback/Queen4.java new file mode 100644 index 0000000..9d3839e --- /dev/null +++ b/src/main/java/com/study/traceback/Queen4.java @@ -0,0 +1,127 @@ +package com.study.traceback; + +/** + * 4皇后问题 + *

+ * 皇后问题研究的是如何将 4个皇后放置在 4×4 的棋盘上,并且使皇后彼此之间不能相互攻击。 + * 每个皇后的攻击路线为皇后所在的同一行和列以及对角线(意味着这些位置不能放置其他皇后) + *

+ * https://leetcode-cn.com/problems/n-queens/ n皇后问题 + * https://mp.weixin.qq.com/s/hTcRURIWs_9YOfwlOjsA3w 8皇后问题 + *

+ * 解决皇后问题,可以分为两个层面: + * 1.找出第一种正确摆放方式,也就是深度优先遍历。 + * 2.找出全部的正确摆放方式,也就是广度优先遍历。 + */ +public class Queen4 { + + // 棋盘长宽和皇后数量(这里棋盘长宽和皇后数量相等) + private static int maxNum = 4; + + // 由于这里使用的是int数组,int的初始值是0,代表没有落子。当有皇后放置的时候,对应的元素值改为1。 + private static int chessBoard[][] = new int[maxNum][maxNum]; + + public static void main(String[] args) { + // 设置棋盘的长宽 + maxNum = 4; + // 从第一行开始,一行一行递归放置皇后 + setQueen(0); + // 打印整个棋盘 输出皇后的位置 + printChessBoard(); + } + + /** + * 定义一个check方法,传入新皇后的落点,通过检查横纵向以及斜向是否存在皇后来判断该位置是否能放. + * + * @param x 新皇后的横坐标 + * @param y 新皇后的纵坐标 + * @return 该位置是否可放新皇后 false不可 true可以 + */ + private static boolean check(int x, int y) { + for (int i = 0; i < y; i++) { + // 检查同一列是否已经有皇后存在(横坐标x相同) + if (chessBoard[x][i] == 1) { + return false; // 同一行存在皇后 + } + // 检查左斜上方是否有皇后, 左斜方坐标为 x-i, y-i + // 由于是从上往下放皇后,所以只需要检测左斜上方是否有皇后即可, 所以x坐标范围是 0 到 x-1 + if (x - 1 - i >= 0 && chessBoard[x - 1 - i][y - 1 - i] == 1) { + return false; //存在皇后 + } + + // 检查右斜上方是否有皇后, 右斜上方的坐标为 x +i, y-i + if (x + 1 + i < maxNum && chessBoard[x + 1 + i][y - 1 - i] == 1) { + return false;//存在皇后 + } + } + // 不存在皇后, 可以放新皇后 + return true; + } + + /** + * 通过递归+回溯进行放置新皇后 + * 在每一行放置新皇后, 一旦放到第5行就说明前面4行皇后都已放置成功,则结束放置 + * + * @param y + * @return + */ + private static boolean setQueen(int y) { + // 一旦放置到第5行的时候 说明放置所有皇后成功,结束递归 + if (y == maxNum) { + System.out.println("所有皇后放置完成"); + return true;// //递归从外往内终止条件, 之后进行从内往外回溯, 回到上一行进行放皇后试验, y-- + } + + // 遍历y所在的当前行,逐一个列进行检查和放置皇后 + for (int x = 0; x < maxNum; x++) { + // 清理当前行的皇后, 重新放置 + // 如果在当前行的某一列放置皇后失败,则需要将当前行之前放置的皇后清除,重新在下一列放置皇后 + for (int i = 0; i < maxNum; i++) { + chessBoard[i][y] = 0; + } + // 检查该位置能否放置皇后 + System.out.printf("----检查第%d行,第%d列能否放置皇后------\r\n", y, x); + + if (check(x, y)) { + // 如果可以, 放置皇后 + chessBoard[x][y] = 1; + System.out.printf("----放置第%d行,第%d列成功------\r\n", y, x); +// printChessBoard(); +// System.out.println("-------------"); + // 同时递归到下一行进行放置皇后,如果返回true,说明所有皇后已放好 + if (setQueen(y + 1)) { + // 递归达到终止条件,开始从内往外回溯, 这里会执行棋盘的行次数 +// System.out.printf("----全部皇后放置完成------\r\n", x, y); +// printChessBoard(); +// System.out.println("-------------"); + return true; //递归从外往内终止条件, 之后进行从内往外回溯, 回到上一行进行放皇后试验, y-- + } else { + // 如果放置所有行失败, 则回溯到上一行,并且上一行之前放置的某列也失败,并从下一列继续尝试检查放置 + //----放置第2行失败------ + //----放置第1行,第2列失败------ + System.out.printf("----放置第%d行,第%d列失败------\r\n", y, x); +// printChessBoard(); +// System.out.println("-------------"); + } + } else { + System.out.printf("----检查发现第%d行,第%d列无法放入皇后------\r\n", y, x); + } + } + // 如果当前行所有列都无法放置新皇后, 否则当前行放置失败 + System.out.printf("----放置第%d行失败------\r\n", y); + // 这里的false会返回给递归中的setQueen()方法 + return false; //递归从外往内终止条件, 之后进行从内往外回溯, 回到上一行进行放皇后试验, y-- + } + + /** + * 打印整个棋盘,输出皇后的位置 + */ + private static void printChessBoard() { + for (int x = 0; x < maxNum; x++) { + for (int y = 0; y < maxNum; y++) { + System.out.print(chessBoard[x][y] + " "); + } + System.out.println(); + } + } +} diff --git a/src/main/java/com/study/traceback/Queen4_2.java b/src/main/java/com/study/traceback/Queen4_2.java new file mode 100644 index 0000000..18140fb --- /dev/null +++ b/src/main/java/com/study/traceback/Queen4_2.java @@ -0,0 +1,85 @@ +package com.study.traceback; + +public class Queen4_2 { + + // 棋盘的行和列, 以及皇后数量 + private static int maxNum = 4; + + private static int[][] chessBoard = new int[maxNum][maxNum]; + + public static void main(String[] args) { + maxNum = 4; + + // 从第0行开始放置皇后 + setQueen(0); + + printChessBoard(); + } + + /** + * 按行进行放置皇后 + * + * @param y 行号 + * @return + */ + private static boolean setQueen(int y) { + if (y == maxNum) + return true; // 结束递归条件 + + // 遍历第y行的每一列, 检查并放置皇后, 如果下一行放置失败同时进行回溯,从下一列重新放置 + for (int x = 0; x < maxNum; x++) { + // 清理当前行的数据, 重新放置 + for (int i = 0; i < maxNum; i++) { + chessBoard[i][y] = 0; + } + + // 检查当前位置能否放皇后 + if (check(x, y)) { + // 放入皇后 + chessBoard[x][y] = 1; + + // 递归进入下一行继续操作, 这里不能返回false + if (setQueen(y + 1)) { + return true; //所有皇后放置成功,递归达到终止条件 y-- + } + } + } + // 当前行都无法放置皇后, 递归达到终止条件 y-- + return false; + } + + + /** + * 检查落子的纵上方和左右斜上方是否有皇后 + * + * @param x 落子的横坐标 + * @param y 落子的纵坐标 + * @return + */ + private static boolean check(int x, int y) { + // 从当前行往上遍历 + for (int i = 0; i < y; i++) { + // 检查纵上方 + if (chessBoard[x][y - 1 - i] == 1) + return false; + // 检查左斜上方 + if (x - 1 - i >= 0 && chessBoard[x - 1 - i][y - 1 - i] == 1) { + return false; + } + // 检查右斜上方 + if (x + 1 + i < maxNum && chessBoard[x + 1 + i][y - 1 - i] == 1) { + return false; + } + } + return true; + } + + private static void printChessBoard() { + for (int x = 0; x < maxNum; x++) { + for (int y = 0; y < maxNum; y++) { + System.out.print(chessBoard[x][y] + " "); + } + System.out.println(); + } + } +} diff --git a/src/main/java/com/study/traceback/QueenN.java b/src/main/java/com/study/traceback/QueenN.java new file mode 100644 index 0000000..701c614 --- /dev/null +++ b/src/main/java/com/study/traceback/QueenN.java @@ -0,0 +1,75 @@ +package com.study.traceback; + +/** + * N皇后问题, 找出一种可以放置n个皇后的方法,使得每个皇后之间无法互相攻击 + * + * 使用深度优先搜索dfs找出一种可以放置皇后的方法 + * + * 具体做法为递归+回溯 + */ +public class QueenN { + + private static int N = 0; + private static int[][] chessBoard; + + public static void main(String[] args) { + //createChessBoard(4); + //createChessBoard(5); + createChessBoard(8); + setQueen(0); + printChessBoard(); + } + + private static void createChessBoard(int n){ + N = n; + chessBoard = new int[N][N]; + } + + private static boolean setQueen(int y) { + if (y == N) + return true; + + for (int x = 0; x < N; x++) { + // 清理当前行的数据 + for (int i = 0; i < N; i++) { + chessBoard[i][y] = 0; + } + + if (check(x, y)) { + chessBoard[x][y] = 1; + + if (setQueen(y + 1)) { + return true; + } + } + } + return false; + } + + private static boolean check(int x, int y) { + // 从当前位置的上一行开始往上遍历 + for (int i = 0; i < y; i++) { + // 检查纵向 + if (chessBoard[x][y - 1 - i] == 1) + return false; + + // 检查左斜 + if (x - 1 - i >= 0 && chessBoard[x - 1 - i][y - 1 - i] == 1) + return false; + + // 检查右斜 + if (x + 1 + i < N && chessBoard[x + 1 + i][y - 1 - i] == 1) + return false; + } + return true; + } + + private static void printChessBoard(){ + for(int x = 0; x < N; x ++){ + for(int y =0; y < N; y++){ + System.out.print(chessBoard[x][y] + " "); + } + System.out.println(); + } + } +} From 7deda475f7b1c951ee2be34a26f93a1af72ebbd1 Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 3 Feb 2020 20:02:39 +0800 Subject: [PATCH 45/74] =?UTF-8?q?=E6=9C=80=E5=A4=A7=E8=BF=9E=E7=BB=AD?= =?UTF-8?q?=E5=AD=90=E5=BA=8F=E5=88=97=E4=B9=8B=E5=92=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/array/MaximumSubarray.java | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 src/main/java/com/study/array/MaximumSubarray.java diff --git a/src/main/java/com/study/array/MaximumSubarray.java b/src/main/java/com/study/array/MaximumSubarray.java new file mode 100644 index 0000000..ef6c9c0 --- /dev/null +++ b/src/main/java/com/study/array/MaximumSubarray.java @@ -0,0 +1,117 @@ +package com.study.array; + +/** + * 最大子序和 + *

+ * 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 + *

+ * 示例: + *

+ * 输入: [-2,1,-3,4,-1,2,1,-5,4], + * 输出: 6 + * 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 + * 进阶: + *

+ * 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 + *

+ * 链接:https://leetcode-cn.com/problems/maximum-subarray + */ +public class MaximumSubarray { + + public static void main(String[] args) { + int[] nums = {-2, 1}; + //int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; + //int result = maxSubArray(nums); + int result = maxSubArray_greedy(nums); + //int result = maxSubArray2(nums); + System.out.println(result); + } + + /** + * 动态规划 + *

+ * 首先对数组进行遍历, 当前最大连续子序列和为currentSum,结果为max; + * 如果 currentSum>0, 说明currentSum + 下一个数 是有增益效果的. + * 如果 currentSum<0, 说明currentSum + 下一个数 没有增益效果. 需要舍弃, 使用下一个数进行替换 + * 比较 currentSum和max值, 取其中最大值作为max + * + * @param nums + * @return + */ + private static int maxSubArray(int[] nums) { + int max = nums[0]; + int currentSum = nums[0]; + for (int i = 1; i < nums.length; i++) { + // currentSum为正数时候, 有增益效果, 加下一个元素的和 比使用下一个元素更大 + if (currentSum > 0) { + currentSum += nums[i]; + } else { + // 为负数时候, 没有增益效果, +下一个元素 小于直接使用下一个元素,所以使用下一个元素替换 重新开始计算currentSum + currentSum = nums[i]; + } + // 取 当前和 最大值 中最大的一个 + max = Math.max(max, currentSum); + } + return max; + } + + /** + * 使用贪心算法 + * + * 遍历每个元素,将当前所有数之和 与 当前元素做比较, 取大的 作为当前所有数之和 + * 同时保持更新最大值, 将当前所有数之和 跟 最大值做比较, 取最大的 更新为 最大值 + * + * 因为只需要一次循环遍历,时间复杂度O(n), 空间复杂度O(1) + * + * @param nums + * @return + */ + private static int maxSubArray_greedy(int[] nums) { + int max = nums[0]; + int currentSum = nums[0]; + for (int i = 1; i < nums.length; i++) { + // 贪心 当前所有数之和 把每个数都加上 + currentSum += nums[i]; + // 如果当前数 比当前所有数之和还大, 那就使用当前数 作为当前所有数之和 + // 如果当前数 比当前所有数之和还小, 那就继续使用当前所有数之和 + // 比如 [-2 1] -2 + 1 < 1, 那就直接从1开始计算 舍弃之前的-2 + currentSum = Math.max(nums[i], currentSum); + // 取 当前所有数之和 与 最大值 中最大的一个 + max = Math.max(max, currentSum); + } + return max; + } + + /** + * 暴力解法, 计算出每个元素的连续最大和, 从中取最大的那个 + * 时间复杂度 O(n*m) + * + * @param nums + * @return + */ + private static int maxSubArray2(int[] nums) { + if (nums.length == 0) + return 0; + + if (nums.length == 1) + return nums[0]; + + int max = nums[0]; + + // 遍历每个元素, 得出每个元素能够得到的最大值和, 从中取最大的 + for (int i = 0; i < nums.length; i++) { + int curSum = nums[i]; + + for (int j = i + 1; j < nums.length; j++) { + // 正数有增益效果, 对之和有帮助 + if (curSum > 0) + curSum += nums[j]; + else// 负数没有增益效果, 需要舍去 + curSum = nums[j]; + + max = Math.max(curSum, max); + } + } + return max; + } +} From fe5b28d127cb8afa2604e4d846bc9bca90aa1dfe Mon Sep 17 00:00:00 2001 From: longwu Date: Tue, 4 Feb 2020 13:55:27 +0800 Subject: [PATCH 46/74] =?UTF-8?q?=E6=9C=80=E9=95=BF=E5=85=AC=E5=85=B1?= =?UTF-8?q?=E5=AD=90=E5=BA=8F=E5=88=97LCS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LongestCommonSubsequence.java | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 src/main/java/com/study/dynamicprogramming/LongestCommonSubsequence.java diff --git a/src/main/java/com/study/dynamicprogramming/LongestCommonSubsequence.java b/src/main/java/com/study/dynamicprogramming/LongestCommonSubsequence.java new file mode 100644 index 0000000..aa83446 --- /dev/null +++ b/src/main/java/com/study/dynamicprogramming/LongestCommonSubsequence.java @@ -0,0 +1,137 @@ +package com.study.dynamicprogramming; + +/** + * 最长公共子序列 + *

+ * 给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列。 + *

+ * 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 + * 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。 + *

+ * 若这两个字符串没有公共子序列,则返回 0。 + *

+ * 示例 1: + *

+ * 输入:text1 = "abcde", text2 = "ace" + * 输出:3 + * 解释:最长公共子序列是 "ace",它的长度为 3。 + * 示例 2: + *

+ * 输入:text1 = "abc", text2 = "abc" + * 输出:3 + * 解释:最长公共子序列是 "abc",它的长度为 3。 + * 示例 3: + *

+ * 输入:text1 = "abc", text2 = "def" + * 输出:0 + * 解释:两个字符串没有公共子序列,返回 0。 + *

+ * 链接:https://leetcode-cn.com/problems/longest-common-subsequence + */ +public class LongestCommonSubsequence { + + public static void main(String[] args) { + String text1 = "abc"; + String text2 = "def"; + +// String text1 = "abcde"; +// String text2 = "ace"; + +// String text1 = "oxcpqrsvwf"; +// String text2 = "shmtulqrypy"; + int result = longestCommonSubsequence(text1, text2); + System.out.println(result); + } + + /** + * 使用动态规划 dp方程来解 + * 具体操作: 将两个字符串x和y转到一个二维数组操做. + * 第一步: 定义basecase: 由于dp方程需要从0开始,让索引为0的长和宽的列表示空字符串,所以数组的长i和宽j为字符串长度+1. + * dp[0][i]和dp[j][0]都应该初始化为0. + * 比如 dp[0][3] = 0 的含义是, 对于字符串""和"abc", 它们的最长公共子序列lcs为0 + * 第二步: 找状态转移dp方程: + * i=0或者j=0 LCS(i,j)=0 //如果两个字符中有有一个为空, 那LCS=0 + * i>0且j>0 且Xi=Yj LCS(i,j)=LCS(i-1,j-1) + 1 // 如果两个字符相等, 那么LCS为其相邻左斜方值+1 + * i>0且j>0 且Xi!=Yj LCS(i,j)=MAX{(LCS(i-1,j),LCS(i,j-1)} //如果连个字符不相等, 那么LCS为其相邻的左和上方值中最大的一个 + * + * @param text1 + * @param text2 + * @return + */ + public static int longestCommonSubsequence(String text1, String text2) { + // 创建一个dp二维数组, 长宽为两个字符串的长度+1 + int x = text1.length() + 1; + int y = text2.length() + 1; + int[][] dp = new int[x][y]; + // 从数组的索引为1位置开始, 是为了使用根据索引为0的位置(空字符串)的值进行推倒 + // 数组中长和宽为0的位置, 所有值默认为0 + for (int i = 1; i < x; i++) { + for (int j = 1; j < y; j++) { + // 如果字符text1[i] == text2[j], 那么对应的dp[i][j] = dp[i-1][j-1] + 1 相邻左斜方值+1 + if (text1.charAt(i - 1) == text2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + // 如果字符text1[i] != text2[j], 那么对应的dp[i][j] = max(dp[i-1][j], dp[i][j-1]) 取相邻左和上值中最大的一个 + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + } + } + } + return dp[x - 1][y - 1]; + } + + + public static int longestCommonSubsequence_2(String text1, String text2) { + int x = text1.length(); + int y = text2.length(); + + int[][] dp = new int[x + 1][y + 1]; + for (int i = 1; i <= x; i++) { + for (int j = 1; j <= y; j++) { + if (text1.charAt(i-1) == text2.charAt(j-1)) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + } + } + } + return dp[x][y]; + } + + +// public static int longestCommonSubsequence(String text1, String text2) { +// int maxLength = 0; +// // 记录短的字符数组开始计算的位置 +// int shortPosition = 0; +// +// char[] chars1 = text1.toCharArray(); +// char[] chars2 = text2.toCharArray(); +// +// char[] longChars = new char[]{}; +// char[] shortChars = new char[]{}; +// +// if (chars1.length >= chars2.length) { +// longChars = chars1; +// shortChars = chars2; +// } else { +// longChars = chars2; +// shortChars = chars1; +// } +// +// int currentLength = 0; +// for (int i = 0; i < longChars.length; i++) { +// System.out.println("longChars: " + i); +// int j = shortPosition; +// while (j < shortChars.length) { +// if (longChars[i] == shortChars[j]) { +// currentLength++; +// shortPosition = j + 1; //修改下一次开始检查的位置 +// +// maxLength = Math.max(maxLength, currentLength); +// break; +// } +// j++; +// } +// } +// return maxLength; +// } +} From 682936494501b74eed48f5e1b7bc7b4df44e8290 Mon Sep 17 00:00:00 2001 From: longwu Date: Tue, 4 Feb 2020 18:17:32 +0800 Subject: [PATCH 47/74] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96,?= =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/study/string/LongestPalindrome.java | 126 ++++++++++++++---- 1 file changed, 99 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/study/string/LongestPalindrome.java b/src/main/java/com/study/string/LongestPalindrome.java index 790ef09..a8beee1 100644 --- a/src/main/java/com/study/string/LongestPalindrome.java +++ b/src/main/java/com/study/string/LongestPalindrome.java @@ -5,54 +5,126 @@ import java.util.Map; /** - * 计算最长回文串的字符串以及字符个数 + * 最长回文串 (本题可以将字符串拆解重新构造出字符串) *

- * 回文串是一个正读和反读都一样的字符串。对一个左边的字符 i 右边一定会有一个对称 i。比如 'abcba', 'aa','bb' 这几个回文串。其中第一个有点特殊,中间的 c 是唯一的。 - * 如果让你来造一个回文串你会怎么造? 首先让它左右两边绝对对称,如果可能的话再加上一个唯一的中心。 + * 给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。 *

+ * 在构造过程中,请注意区分大小写。比如 "Aa" 不能当做一个回文字符串。 *

+ * 注意: + * 假设字符串的长度不会超过 1010。 *

- * 链接:https://leetcode-cn.com/problems/longest-palindrome/solution/zui-chang-hui-wen-chuan-by-leetcode/ + * 示例 1: + *

+ * 输入: + * "abccccdd" + *

+ * 输出: + * 7 + *

+ * 解释: + * 我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。 + *

+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/longest-palindrome */ public class LongestPalindrome { + /** + * 本题允许将字符串拆解后构造字符串, 所以只需要计算每个字符出现的次数即可 + * + * @param args + */ public static void main(String[] args) { //String s = "abcbad"; //String s = "a"; - String s = "abccccdd"; - System.out.println(1 / 2 * 2); - System.out.println(3 / 2 * 2); - System.out.println(5 / 2 * 2); - System.out.println(0 / 2 * 2); + //String s = "abccccdd"; + String s = "Aabcfecd"; +// System.out.println(1 / 2 * 2); +// System.out.println(3 / 2 * 2); +// System.out.println(5 / 2 * 2); +// System.out.println(0 / 2 * 2); + System.out.println(1 % 2); // 1 % 2 = 1; System.out.println("--------------------"); - LongestPalindrome lp = new LongestPalindrome(); - //System.out.println(lp.longestPalindrome(s)); - System.out.println(lp.count(s)); - System.out.println("最长回文字符串为: " + lp.getLongestPalindrome(s)); + //System.out.println(longestPalindrome(s)); + System.out.println(longestPalindrome_2(s)); + //System.out.println(longestPalindrome2(s)); + //System.out.println(longestPalindrome3(s)); } - public int longestPalindrome(String s) { - // ASCII总共128个字符 - int[] count = new int[128]; + /** + * 使用贪心法计算每个字符的个数, 取它的偶数个数(实现偶数对称回文数), 在偶数个存在的情况下可以再加上1个字符放在中间(实现奇数对称回文数) + * 这里用到了char的ASCII值(hash值)来定位每个char在数组中的位置 比如 'A'hash值为65, 'a' hash值为97, 而'z'的hash值为122 + * 由于ASCII一共有128个字符, 所以创建了一个长度为128的数组来存. + *

+ * 当然也可以优化一下, 由于所有的大小写字符所处的位置是65 到 122, 所以只需要使用长度为58的数组来存即可, 存的时候 每个字符需要减去第65个字符'A' 得出范围0-58的索引 + *

+ * 使用一个变量 length来计算满足条件的字符个数, 遍历字符数组, 得出每个字符的偶数个(0 2 4 ...), 加到总个数length中, 如果当前length总个数为偶数个, 那么还能再加一个字符作为中间对称字符 + *

+ *

+ * 时间复杂度O(n), 最多用了一层循环 + * 空间复杂度O(n), 用到了数组 + *

+ * ASCII码表 http://ascii.911cha.com/ + * + * @param s + * @return + */ + public static int longestPalindrome(String s) { + // ASCII总共128个字符, 所以使用长度为128的数组来存 +// int[] count = new int[128]; +// for (char c : s.toCharArray()) { +// count[c]++; //记录每个char的数量 +// } + + // 也可以对数组进行优化, 使用一个长度为58的数组来存, 因为大小写字母位于ASCII码的第65 到 122位, 刚好是58个, 其中A为第65位 + int[] count = new int[58]; for (char c : s.toCharArray()) { - count[c]++; //记录每个char的数量 + // 每个字符都比A大 0-58 + count[c - 'A']++; //记录每个char的数量 } - int ans = 0; + // 统计满足条件的字符个数 + int length = 0; for (int v : count) { - // 统计元素出现的总个数, 必须是出现个数>=2以上的才满足, 而且取其最大偶数次数, 比如2次的ans=2, 3次的ans=2,5次的ans=4 - ans += v / 2 * 2; - // 用于找出中心字符, 条件是中心字符出现次数为奇数次比如1 3 5等,且当前其他元素出现的总次数为偶数(如 0 2 4等)就满足条件 - if (v % 2 == 1 && ans % 2 == 0) - ans++; + // 找出每个字符的偶数个, 比如 aaaaa出现了5次,取偶数次为 aaaa 4次 5 / 2 *2 = 2 * 2 = 4 + // 当然如果该字符次数不足2, 则结果为0 比如 1 / 2 * 2 = 0*2 = 0 这里巧妙的用到了整数除法无法整除得到0的技巧 + length += v / 2 * 2; + // 如果当前length总个数为偶数个, 那么还能再加一个字符作为中间对称字符 + if (v % 2 == 1 && length % 2 == 0) // 判断奇数个的前提是当前字符个数为奇数个, 可以是1 3 5 ...,且满足条件的字符总个数为偶数 + length++; } - return ans; + return length; } + public static int longestPalindrome_2(String s) { + int[] chars = new int[58]; - public int count(String s) { + for (char c : s.toCharArray()) { + chars[c - 'A']++; + } + + int length = 0; + for (int c : chars) { + length += c / 2 * 2; //获取偶数个数字符 + + // 在当前满足条件的字符总个数为偶数的情况下, 可以再增加一个奇数作为中间对称字符 + if (length % 2 == 0 && c % 2 == 1) + length++; + } + + return length; + } + + /** + * 贪心算法, 使用hashmap来存储每个字符 + * + * @param s + * @return + */ + public static int longestPalindrome2(String s) { // 记录每个char出现的次数 Map kv = new HashMap<>(); for (char c : s.toCharArray()) { @@ -67,7 +139,7 @@ public int count(String s) { for (Map.Entry entry : kv.entrySet()) { // 统计元素出现的总个数, 必须是出现个数>=2以上的才满足, 而且取其最大偶数次数, 比如2次的count=2, 3次的count=2,5次的count=4 count += entry.getValue() / 2 * 2; - // 只有之前出现的总个数为偶数, 才允许将出现一次的元素加到总数 + // 只有之前出现的总个数为偶数(0 2 4), 才允许将出现一次的元素加到总数 if (count % 2 == 0 && entry.getValue() % 2 == 1) { count++; } @@ -81,7 +153,7 @@ public int count(String s) { * @param s * @return */ - public String getLongestPalindrome(String s) { + public static String longestPalindrome3(String s) { StringBuilder result = new StringBuilder(); // 记录每个char出现的次数 Map kv = new HashMap<>(); From 351c6d5c68fe58390eb702f7b0fddf7cd3b9d054 Mon Sep 17 00:00:00 2001 From: longwu Date: Thu, 6 Feb 2020 13:50:50 +0800 Subject: [PATCH 48/74] =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/binarytree/InorderTraversal.java | 5 +- .../com/study/binarytree/LevelTraversal.java | 5 +- .../binarytree/LowestCommonAncestor.java | 3 +- .../com/study/binarytree/MaximumDepth.java | 29 ++-- .../com/study/binarytree/MinimumDepth.java | 5 +- .../com/study/binarytree/NodesInALevel.java | 6 +- .../study/binarytree/PostorderTraversal.java | 5 +- .../study/binarytree/PreorderTraversal.java | 5 +- .../{ReverseTree.java => TraverseTree.java} | 7 +- .../java/com/study/binarytree/TreeNode.java | 4 +- .../LowestCommonAncestor.java | 21 +-- .../binarysearchtree/SearchTreeNode.java | 15 -- .../ValidateBinarySearchTree.java | 106 +++++++++++--- src/main/java/com/study/utils/TreeUtils.java | 133 +++++++++++++++--- 14 files changed, 247 insertions(+), 102 deletions(-) rename src/main/java/com/study/binarytree/{ReverseTree.java => TraverseTree.java} (95%) delete mode 100644 src/main/java/com/study/binarytree/binarysearchtree/SearchTreeNode.java diff --git a/src/main/java/com/study/binarytree/InorderTraversal.java b/src/main/java/com/study/binarytree/InorderTraversal.java index d55e87c..06907c2 100644 --- a/src/main/java/com/study/binarytree/InorderTraversal.java +++ b/src/main/java/com/study/binarytree/InorderTraversal.java @@ -28,10 +28,9 @@ public class InorderTraversal { public static void main(String[] args) { - int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - List treeNodes = TreeUtils.buildTree(arr); - TreeNode root = treeNodes.get(0); + TreeNode root = TreeUtils.buildTree(arr); //List list = inorderTraversal(root); //List list = inorderTraversal2(root); diff --git a/src/main/java/com/study/binarytree/LevelTraversal.java b/src/main/java/com/study/binarytree/LevelTraversal.java index fab377e..3955690 100644 --- a/src/main/java/com/study/binarytree/LevelTraversal.java +++ b/src/main/java/com/study/binarytree/LevelTraversal.java @@ -20,9 +20,8 @@ */ public class LevelTraversal { public static void main(String[] args) { - int[] arr = {1, 2, 3, 4, 5, 6, 7}; - List treeNodes = TreeUtils.buildTree(arr); - TreeNode root = treeNodes.get(0); + Integer[] arr = {1, 2, 3, 4, 5, 6, 7}; + TreeNode root = TreeUtils.buildTree(arr); //List> allNodes = levelOrder(root); List> allNodes = levelOrder2(root); diff --git a/src/main/java/com/study/binarytree/LowestCommonAncestor.java b/src/main/java/com/study/binarytree/LowestCommonAncestor.java index ec4a7f2..f763b54 100644 --- a/src/main/java/com/study/binarytree/LowestCommonAncestor.java +++ b/src/main/java/com/study/binarytree/LowestCommonAncestor.java @@ -38,8 +38,7 @@ public class LowestCommonAncestor { public static void main(String[] args) { int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - List treeNodes = TreeUtils.buildTree(arr); - + List treeNodes = TreeUtils.buildTreeAndList(arr); //TreeNode p = treeNodes.get(4); //TreeNode q = treeNodes.get(5); diff --git a/src/main/java/com/study/binarytree/MaximumDepth.java b/src/main/java/com/study/binarytree/MaximumDepth.java index 276982d..057d893 100644 --- a/src/main/java/com/study/binarytree/MaximumDepth.java +++ b/src/main/java/com/study/binarytree/MaximumDepth.java @@ -31,15 +31,17 @@ public class MaximumDepth { /** * 可以通过深度优先进行递归 或者 层级遍历 + * * @param args */ public static void main(String[] args) { - int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - List treeNodes = TreeUtils.buildTree(arr); - TreeNode root = treeNodes.get(0); + //Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + Integer[] arr = {5, 1, 4, null, null, 3, 6}; + TreeNode root = TreeUtils.buildTree(arr); + TreeUtils.show(root); //System.out.println(maxDepthByRecursion(root, "", root.val)); - System.out.println(maxDepthByLoop(root)); + //System.out.println(maxDepthByLoop(root)); System.out.println(maxDepthByRecursion2(root)); } @@ -75,22 +77,29 @@ private static int maxDepthByRecursion(TreeNode root, String position, int val) return result; } - - private static int maxDepthByRecursion2(TreeNode root){ - if(root == null) + /** + * 深度优先遍历, 计算每层左右节点的层数,取最大值 + * + * 时间复杂度为O(n) + * + * @param root + * @return + */ + private static int maxDepthByRecursion2(TreeNode root) { + if (root == null) return 0; int left = maxDepthByRecursion2(root.left); int right = maxDepthByRecursion2(root.right); - return Math.max(left,right) + 1; + // 从最底层往根节点回溯的过程中, 计算每层的层数, 取左右两边最大的层数并+1(每层的层数) + return Math.max(left, right) + 1;// 最底层为0+1=1, 倒数第2层为0+1+1=2 } /** * 使用广度优先的方式找出最大深度 遍历+队列 - *

- * 需要将左右子树的最大深度进行对比, 求最大的 + * 时间复杂度为O(n) * * @param root * @return diff --git a/src/main/java/com/study/binarytree/MinimumDepth.java b/src/main/java/com/study/binarytree/MinimumDepth.java index 84f9f05..471713b 100644 --- a/src/main/java/com/study/binarytree/MinimumDepth.java +++ b/src/main/java/com/study/binarytree/MinimumDepth.java @@ -32,9 +32,8 @@ public class MinimumDepth { public static void main(String[] args) { - int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - List treeNodes = TreeUtils.buildTree(arr); - TreeNode root = treeNodes.get(0); + Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + TreeNode root = TreeUtils.buildTree(arr); //System.out.println(minDepthByRecursion(root)); System.out.println(minDepthByLoop(root)); diff --git a/src/main/java/com/study/binarytree/NodesInALevel.java b/src/main/java/com/study/binarytree/NodesInALevel.java index 29a8408..bd04267 100644 --- a/src/main/java/com/study/binarytree/NodesInALevel.java +++ b/src/main/java/com/study/binarytree/NodesInALevel.java @@ -18,9 +18,9 @@ */ public class NodesInALevel { public static void main(String[] args) { - int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - List treeNodes = TreeUtils.buildTree(arr); - TreeNode root = treeNodes.get(0); + Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + + TreeNode root = TreeUtils.buildTree(arr); int k = 2; // System.out.printf("第%d层节点数:%d\r\n", k, k_nodes(root, k)); diff --git a/src/main/java/com/study/binarytree/PostorderTraversal.java b/src/main/java/com/study/binarytree/PostorderTraversal.java index 336900c..cda9930 100644 --- a/src/main/java/com/study/binarytree/PostorderTraversal.java +++ b/src/main/java/com/study/binarytree/PostorderTraversal.java @@ -27,9 +27,8 @@ */ public class PostorderTraversal { public static void main(String[] args) { - int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - List treeNodes = TreeUtils.buildTree(arr); - TreeNode root = treeNodes.get(0); + Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + TreeNode root = TreeUtils.buildTree(arr); //List list = postorderTraversal(root); //List list = postorderTraversal2(root); diff --git a/src/main/java/com/study/binarytree/PreorderTraversal.java b/src/main/java/com/study/binarytree/PreorderTraversal.java index fd147a0..cb60774 100644 --- a/src/main/java/com/study/binarytree/PreorderTraversal.java +++ b/src/main/java/com/study/binarytree/PreorderTraversal.java @@ -28,9 +28,8 @@ */ public class PreorderTraversal { public static void main(String[] args) { - int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - List treeNodes = TreeUtils.buildTree(arr); - TreeNode root = treeNodes.get(0); + Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + TreeNode root = TreeUtils.buildTree(arr); //List list = preorderTraversal(root); //List list = preorderTraversalByLoop(root); diff --git a/src/main/java/com/study/binarytree/ReverseTree.java b/src/main/java/com/study/binarytree/TraverseTree.java similarity index 95% rename from src/main/java/com/study/binarytree/ReverseTree.java rename to src/main/java/com/study/binarytree/TraverseTree.java index ae7a774..0b4daf8 100644 --- a/src/main/java/com/study/binarytree/ReverseTree.java +++ b/src/main/java/com/study/binarytree/TraverseTree.java @@ -11,11 +11,10 @@ *

* 其中每种遍历 又有多种遍历方法 比如递归, 迭代 */ -public class ReverseTree { +public class TraverseTree { public static void main(String[] args) { - int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - List treeNodes = TreeUtils.buildTree(arr); - TreeNode root = treeNodes.get(0); + Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + TreeNode root = TreeUtils.buildTree(arr); //preOrderReverse(root); // 0 1 3 7 4 2 5 6 //preOrderReverse2(root); // 0 1 3 7 4 2 5 6 diff --git a/src/main/java/com/study/binarytree/TreeNode.java b/src/main/java/com/study/binarytree/TreeNode.java index 7a40ec2..6a70e87 100644 --- a/src/main/java/com/study/binarytree/TreeNode.java +++ b/src/main/java/com/study/binarytree/TreeNode.java @@ -3,9 +3,9 @@ public class TreeNode { public TreeNode left; public TreeNode right; - public int val; + public Integer val; - public TreeNode(int val) { + public TreeNode(Integer val) { this.val = val; } } diff --git a/src/main/java/com/study/binarytree/binarysearchtree/LowestCommonAncestor.java b/src/main/java/com/study/binarytree/binarysearchtree/LowestCommonAncestor.java index efdca52..f3a2e90 100644 --- a/src/main/java/com/study/binarytree/binarysearchtree/LowestCommonAncestor.java +++ b/src/main/java/com/study/binarytree/binarysearchtree/LowestCommonAncestor.java @@ -1,5 +1,6 @@ package com.study.binarytree.binarysearchtree; +import com.study.binarytree.TreeNode; import com.study.utils.TreeUtils; import java.util.List; @@ -37,15 +38,15 @@ public static void main(String[] args) { // 由于二叉搜索树也是二叉树, 所以既可以使用二叉搜索树的方法也可以使用二叉树的方法求最近公共祖先 int[] arr = {5, 3, 7, 1, 4, 6, 8}; - List treeNodes = TreeUtils.buildSearchTree(arr); + List treeNodes = TreeUtils.buildTreeAndList(arr); - SearchTreeNode root = treeNodes.get(0); + TreeNode root = treeNodes.get(0); - SearchTreeNode p = treeNodes.get(5); - SearchTreeNode q = treeNodes.get(6); + TreeNode p = treeNodes.get(5); + TreeNode q = treeNodes.get(6); - //SearchTreeNode lca = getlca(root, p, q); - SearchTreeNode lca = getlca2(root, p, q); + //TreeNode lca = getlca(root, p, q); + TreeNode lca = getlca2(root, p, q); if (lca != null) System.out.println(lca.val); @@ -58,7 +59,7 @@ public static void main(String[] args) { * @param q * @return */ - private static SearchTreeNode getlca(SearchTreeNode root, SearchTreeNode p, SearchTreeNode q) { + private static TreeNode getlca(TreeNode root, TreeNode p, TreeNode q) { if (root == null) return null; @@ -67,10 +68,10 @@ private static SearchTreeNode getlca(SearchTreeNode root, SearchTreeNode p, Sear // 递归根左子树, 查找p或q节点, 一旦找到立刻退出, 进行根右子树递归 // 因为根左子树找到1个节点, 那么只要根右子树存在另一个节点, 那最近公共祖先就是根节点, 否则就是左子树找到的节点本身(另一个节点肯定在这个节点下面) - SearchTreeNode left = getlca(root.left, p, q); + TreeNode left = getlca(root.left, p, q); // 递归右子树, 查看右边是否也存在目标节点 - SearchTreeNode right = getlca(root.right, p, q); + TreeNode right = getlca(root.right, p, q); // 如果左右都存在, 直接返回当前节点 if (left != null && right != null) @@ -92,7 +93,7 @@ private static SearchTreeNode getlca(SearchTreeNode root, SearchTreeNode p, Sear * @param q * @return */ - private static SearchTreeNode getlca2(SearchTreeNode root, SearchTreeNode p, SearchTreeNode q) { + private static TreeNode getlca2(TreeNode root, TreeNode p, TreeNode q) { // 如果 p和q的值 都小于 root, 直接从树的左边去找 if (p.val < root.val && root.val > q.val) return getlca2(root.left, p, q); diff --git a/src/main/java/com/study/binarytree/binarysearchtree/SearchTreeNode.java b/src/main/java/com/study/binarytree/binarysearchtree/SearchTreeNode.java deleted file mode 100644 index f325324..0000000 --- a/src/main/java/com/study/binarytree/binarysearchtree/SearchTreeNode.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.study.binarytree.binarysearchtree; - -/** - * 二叉搜索树节点 - */ -public class SearchTreeNode { - public SearchTreeNode left; - public SearchTreeNode right; - public SearchTreeNode parent; - public int val; - - public SearchTreeNode(int val) { - this.val = val; - } -} diff --git a/src/main/java/com/study/binarytree/binarysearchtree/ValidateBinarySearchTree.java b/src/main/java/com/study/binarytree/binarysearchtree/ValidateBinarySearchTree.java index 25a571b..d862775 100644 --- a/src/main/java/com/study/binarytree/binarysearchtree/ValidateBinarySearchTree.java +++ b/src/main/java/com/study/binarytree/binarysearchtree/ValidateBinarySearchTree.java @@ -1,5 +1,6 @@ package com.study.binarytree.binarysearchtree; +import com.study.binarytree.TreeNode; import com.study.utils.TreeUtils; import java.util.ArrayList; @@ -36,26 +37,48 @@ * 输出: false * 解释: 输入为: [5,1,4,null,null,3,6]。 * 根节点的值为 5 ,但是其右子节点值为 4 。 + *

+ * https://leetcode-cn.com/problems/validate-binary-search-tree/ */ public class ValidateBinarySearchTree { + /** + * 节点的左子树只包含小于当前节点的数。 + * 节点的右子树只包含大于当前节点的数。 + * 所有左子树和右子树自身必须也是二叉搜索树。 + * + * @param args + */ public static void main(String[] args) { - int[] arr = {5, 3, 7, 1, 4, 6, 8}; - - List treeNodes = TreeUtils.buildSearchTree(arr); + //Integer[] arr = {5, 3, 7, 1, 4, 6, 8}; + //Integer[] arr = {1, 1}; - List nodes = new ArrayList(); + Integer[] arr = {5, 1, 6, null, null, 3, 7}; + //Integer[] arr = {3, 9, 20, null, null, 15, 7}; - SearchTreeNode root = treeNodes.get(0); + TreeNode root = TreeUtils.buildTree(arr); + System.out.println("中序遍历-----"); + List nodes = new ArrayList(); inorder(root, nodes); // 验证树是否正确,从小到大输出 - for (SearchTreeNode node : nodes) { + for (TreeNode node : nodes) { System.out.print(node.val + " "); } + System.out.println("\r\n中序遍历-----"); - System.out.println(isValidBST(root)); + System.out.println("打印出树的结构-----"); + // 将刚刚创建的树打印出来 + TreeUtils.show(root); + System.out.println("打印出树的结构-----"); - //System.out.println(isValidBST(root, 1, 8)); - System.out.println(isValidBST2(root, 1, 8)); + // 中序遍历获取所有节点 判断是否为有序节点 + //System.out.println(isValidBST_InOrder(root)); + //System.out.println(isValidBST_InOrder_2(root)); + + // 递归的方式判断是否所有子树 都满足 左子树<节点<右子树 + System.out.println(isValidBST(root, null, null)); + //System.out.println(isValidBST_2(root, null, null)); + + //System.out.println(isValidBST2(root)); } @@ -71,8 +94,8 @@ public static void main(String[] args) { * @param root * @return */ - private static boolean isValidBST(SearchTreeNode root) { - List nodes = new ArrayList(); + private static boolean isValidBST_InOrder(TreeNode root) { + List nodes = new ArrayList(); inorder(root, nodes); // 验证树是否正确,从小到大输出 for (int i = 0; i < nodes.size() - 1; i++) { @@ -84,13 +107,34 @@ private static boolean isValidBST(SearchTreeNode root) { return true; } + private static boolean isValidBST_InOrder_2(TreeNode root) { + List nodes = new ArrayList<>(); + // 中序遍历后, 返回的节点为从小到大有序的 + inorder_2(root, nodes); + + for (int i = 0; i < nodes.size() - 1; i++) { + // 如果后面的节点大于前面的节点 说明它不是二叉搜索树 + if (nodes.get(i).val >= nodes.get(i + 1).val) { + return false; + } + } + return true; + } + /** - * 验证是否为二叉搜索树, 可以根据它的 左子节点 < 父节点 < 右子节点的特性来 判断每个节点是否都满足 + * 验证是否为二叉搜索树, 可以判断左边所有与节点值是否小于根节点, 右边所有节点值 大于 根节点. + * 以及每棵子树 都是 左子树 < 节点 < 右子树 + *

+ * 深度优先搜索: 使用递归的方式遍历左右子树, 判断是否每个节点 都满足 左子树 < 节点 < 右子树 + *

+ * 节点的左子树只包含小于当前节点的数。 + * 节点的右子树只包含大于当前节点的数。 + * 所有左子树和右子树自身必须也是二叉搜索树。 * * @param root * @return */ - private static boolean isValidBST(SearchTreeNode root, Integer min, Integer max) { + private static boolean isValidBST(TreeNode root, Integer min, Integer max) { if (root == null) //如果root为空 说明当前节点已经递归到最底层了 或者树本身就是空树 return true; if (min != null && root.val < min) //如果root小于最小值, 不是二叉搜索树 @@ -98,16 +142,25 @@ private static boolean isValidBST(SearchTreeNode root, Integer min, Integer max) if (max != null && root.val > max) //如果root大于最大值, 也不是二叉搜索树 return false; - //左子树的所有节点肯定都比根节点小 右子树的所有节点都比根大 - boolean left = isValidBST(root.left, min, root.val);// 左子树跟节点的值为最大值 - boolean right = isValidBST(root.right, root.val, max); //右子树跟节点为最小值 + System.out.print(root.val + " "); + + // 是否左子树的所有节点肯定都比根节点小 右子树的所有节点都比根大 + boolean left = isValidBST(root.left, min, root.val); + boolean right = isValidBST(root.right, root.val, max); + + if (!left) { + System.out.println(String.format("不满足的左子树节点: node: %d, min: %d", root ==null || root.left == null || root.left.val == null ? "空节点" : root.left.val, min)); + } + + if(!right){ + System.out.println(String.format("不满足的右子树节点: node: %d, max: %d", root ==null || root.right == null || root.right.val == null ? "空节点" : root.left.val, max)); + } - return left && right; //如果左边和右边都满足条件, 则说明该数为平衡二叉树 + return left && right; //如果左边和右边都满足条件, 则说明该数为二叉搜索树 //return isValidBST(root.left, min, root.val) && isValidBST(root.right, root.val, max); } - - private static boolean isValidBST2(SearchTreeNode root, Integer min, Integer max) { + private static boolean isValidBST_2(TreeNode root, Integer min, Integer max) { if (root == null) return true; if (min != null && root.val < min) //跟节点不能小于最小值 @@ -115,11 +168,9 @@ private static boolean isValidBST2(SearchTreeNode root, Integer min, Integer max if (max != null && root.val > max) //跟节点也不能大于最大值 return false; - //不停的递归左子树 和 右子树, 判断是否有不满足条件(左<父<右)的节点存在 - return isValidBST2(root.left, min, root.val) && isValidBST2(root.right, max, root.val); + return isValidBST_2(root.left, min, root.val) && isValidBST_2(root.right, root.val, max); } - /** * 使用中序遍历 获取二叉树每个节点 *

@@ -127,7 +178,16 @@ private static boolean isValidBST2(SearchTreeNode root, Integer min, Integer max * * @param root */ - private static void inorder(SearchTreeNode root, List nodes) { + private static void inorder(TreeNode root, List nodes) { + if (root != null) { + inorder(root.left, nodes); + nodes.add(root); + inorder(root.right, nodes); + } + } + + + private static void inorder_2(TreeNode root, List nodes) { if (root != null) { inorder(root.left, nodes); nodes.add(root); diff --git a/src/main/java/com/study/utils/TreeUtils.java b/src/main/java/com/study/utils/TreeUtils.java index bf35330..b3273d0 100644 --- a/src/main/java/com/study/utils/TreeUtils.java +++ b/src/main/java/com/study/utils/TreeUtils.java @@ -1,7 +1,6 @@ package com.study.utils; import com.study.binarytree.TreeNode; -import com.study.binarytree.binarysearchtree.SearchTreeNode; import java.util.ArrayList; import java.util.List; @@ -9,12 +8,36 @@ public final class TreeUtils { /** - * 构建二叉树, + * 构建二叉树并返回根节点, + * + * 使用层级遍历的方式 一层层创建 + * + * @param nodes + * @return 返回二叉树的根节点 + */ +// public static TreeNode buildTree(int[] nodes) { +// List treeNodes = new ArrayList(); +// for (int data : nodes) { +// treeNodes.add(new TreeNode(data)); +// } +// //创建一个二叉树 +// //创建其他节点 每个节点的左子节点都是这个节点的2倍+1, 右子节点都是这个节点的2倍+2 +// for (int i = 0; i < nodes.length / 2; i++) { +// treeNodes.get(i).left = treeNodes.get(i * 2 + 1); +// if (i * 2 + 2 < treeNodes.size()) { +// treeNodes.get(i).right = treeNodes.get(i * 2 + 2); +// } +// } +// return treeNodes.get(0); +// } + + /** + * 构建二叉树并返回所有节点 * * @param nodes - * @return 返回二叉树的所有节点, root为第0个 + * @return 所有节点 */ - public static List buildTree(int[] nodes){ + public static List buildTreeAndList(int[] nodes) { List treeNodes = new ArrayList(); for (int data : nodes) { treeNodes.add(new TreeNode(data)); @@ -30,26 +53,100 @@ public static List buildTree(int[] nodes){ return treeNodes; } - /** - * 构建二叉搜索树 - * - * @param nodes - * @return 返回二叉搜索树的所有节点, root为第0个 - */ - public static List buildSearchTree(int[] nodes){ - List treeNodes = new ArrayList(); - for (int data : nodes) { - treeNodes.add(new SearchTreeNode(data)); + public static TreeNode buildTree(Integer[] nodes) { + List treeNodes = new ArrayList(); + for (Integer data : nodes) { + treeNodes.add(new TreeNode(data)); } //创建一个二叉树 - SearchTreeNode root = treeNodes.get(0); //创建其他节点 每个节点的左子节点都是这个节点的2倍+1, 右子节点都是这个节点的2倍+2 for (int i = 0; i < nodes.length / 2; i++) { - treeNodes.get(i).left = treeNodes.get(i * 2 + 1); + if (treeNodes.get(i * 2 + 1).val != null) { + treeNodes.get(i).left = treeNodes.get(i * 2 + 1); + } if (i * 2 + 2 < treeNodes.size()) { - treeNodes.get(i).right = treeNodes.get(i * 2 + 2); + if (treeNodes.get(i * 2 + 2).val != null) + treeNodes.get(i).right = treeNodes.get(i * 2 + 2); } } - return treeNodes; + return treeNodes.get(0); + } + + + + // 用于获得树的层数 + public static int getTreeDepth(TreeNode root) { + return root == null ? 0 : (1 + Math.max(getTreeDepth(root.left), getTreeDepth(root.right))); + } + + + private static void writeArray(TreeNode currNode, int rowIndex, int columnIndex, String[][] res, int treeDepth) { + // 保证输入的树不为空 + if (currNode == null) return; + // 先将当前节点保存到二维数组中 + res[rowIndex][columnIndex] = String.valueOf(currNode.val); + + // 计算当前位于树的第几层 + int currLevel = ((rowIndex + 1) / 2); + // 若到了最后一层,则返回 + if (currLevel == treeDepth) return; + // 计算当前行到下一行,每个元素之间的间隔(下一行的列索引与当前元素的列索引之间的间隔) + int gap = treeDepth - currLevel - 1; + + // 对左儿子进行判断,若有左儿子,则记录相应的"/"与左儿子的值 + if (currNode.left != null) { + res[rowIndex + 1][columnIndex - gap] = "/"; + writeArray(currNode.left, rowIndex + 2, columnIndex - gap * 2, res, treeDepth); + } + + // 对右儿子进行判断,若有右儿子,则记录相应的"\"与右儿子的值 + if (currNode.right != null) { + res[rowIndex + 1][columnIndex + gap] = "\\"; + writeArray(currNode.right, rowIndex + 2, columnIndex + gap * 2, res, treeDepth); + } + } + + /** + * 打印出二叉树的结构 + * + * 版权声明:本文为CSDN博主「LenFranky」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 + * 原文链接:https://blog.csdn.net/lenfranky/article/details/89645755 + * + * @param root + * @return + */ + public static void show(TreeNode root) { + if (root == null) System.out.println("EMPTY!"); + // 得到树的深度 + int treeDepth = getTreeDepth(root); + + // 最后一行的宽度为2的(n - 1)次方乘3,再加1 + // 作为整个二维数组的宽度 + int arrayHeight = treeDepth * 2 - 1; + int arrayWidth = (2 << (treeDepth - 2)) * 3 + 1; + // 用一个字符串数组来存储每个位置应显示的元素 + String[][] res = new String[arrayHeight][arrayWidth]; + // 对数组进行初始化,默认为一个空格 + for (int i = 0; i < arrayHeight; i ++) { + for (int j = 0; j < arrayWidth; j ++) { + res[i][j] = " "; + } + } + + // 从根节点开始,递归处理整个树 + // res[0][(arrayWidth + 1)/ 2] = (char)(root.val + '0'); + writeArray(root, 0, arrayWidth/ 2, res, treeDepth); + + // 此时,已经将所有需要显示的元素储存到了二维数组中,将其拼接并打印即可 + for (String[] line: res) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < line.length; i ++) { + sb.append(line[i]); + if (line[i].length() > 1 && i <= line.length - 1) { + i += line[i].length() > 4 ? 2: line[i].length() - 1; + } + } + System.out.println(sb.toString()); + } } } From 488d5e25fd398370fda1e121890d4487a840c7ed Mon Sep 17 00:00:00 2001 From: longwu Date: Sun, 9 Feb 2020 23:17:37 +0800 Subject: [PATCH 49/74] =?UTF-8?q?=E5=88=A4=E6=96=AD=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E4=B8=BA=E5=B9=B3=E8=A1=A1=E4=BA=8C=E5=8F=89=E6=A0=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/binarytree/BalancedBinaryTree.java | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 src/main/java/com/study/binarytree/BalancedBinaryTree.java diff --git a/src/main/java/com/study/binarytree/BalancedBinaryTree.java b/src/main/java/com/study/binarytree/BalancedBinaryTree.java new file mode 100644 index 0000000..4e47458 --- /dev/null +++ b/src/main/java/com/study/binarytree/BalancedBinaryTree.java @@ -0,0 +1,138 @@ +package com.study.binarytree; + +import com.study.utils.TreeUtils; + +/** + * 平衡二叉树 + * + * 给定一个二叉树,判断它是否是高度平衡的二叉树。 + *

+ * 本题中,一棵高度平衡二叉树定义为: + *

+ * 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。 + *

+ * 示例 1: + *

+ * 给定二叉树 [3,9,20,null,null,15,7] + *

+ * 3 + * / \ + * 9 20 + * / \ + * 15 7 + * 返回 true 。 + *

+ * 示例 2: + *

+ * 给定二叉树 [1,2,2,3,3,null,null,4,4] + *

+ * 1 + * / \ + * 2 2 + * / \ + * 3 3 + * / \ + * 4 4 + * 返回 false 。 + *

+ * https://leetcode-cn.com/problems/balanced-binary-tree/ + */ +public class BalancedBinaryTree { + + public static void main(String[] args) { + //Integer[] nodes = {3, 9, 20, null, null, 15, 7}; + Integer[] nodes = {1, 2, 2, 3, 3, null, null, 4, 4}; + TreeNode root = TreeUtils.buildTree(nodes); + TreeUtils.show(root); + + System.out.println(isBalanced(root)); + } + + /** + * 使用前序遍历(根左右),先处理根节点获取高度, 再遍历左右子树是否为平衡二叉树. 从顶至底(暴力法) + * 通过比较左右子树最大高度差是否大于1 来判断以此节点为根节点下是否是二叉平衡树 + *

+ * 从顶至底DFS,以每个节点为根节点,递归判断是否是平衡二叉树: + * 若所有根节点都满足平衡二叉树性质,则返回 True ; + * 若其中任何一个节点作为根节点时,不满足平衡二叉树性质,则返回False。 + *

+ * 最差时间复杂度为O(N^2) + *

+ * 作者:jyd + * 链接:https://leetcode-cn.com/problems/balanced-binary-tree/solution/balanced-binary-tree-di-gui-fang-fa-by-jin40789108/ + * + * @param root + * @return + */ + public static boolean isBalanced(TreeNode root) { + // 根节点为空, 说明是空树,肯定是平衡的 + if (root == null) + return true; + + // 先处理根节点, 获取高度 + // 获取每个节点的左右子树的高度 + int left = getHeight(root.left); + int right = getHeight(root.right); + + // 判断当前root节点为根的树是否为平衡二叉树 + if (Math.abs(left - right) > 1) + return false; + + // 再进行左右子树遍历 + // 如果当前树为平衡树, 那么继续判断递归当前树的左右子树是否为平衡二叉树 + return isBalanced(root.left) && isBalanced(root.right); + } + + /** + * 获取一颗树的最大高度 + * + * @param root + * @return + */ + private static int getHeight(TreeNode root) { + if (root == null) + return 0; + + int left = getHeight(root.left); + + int right = getHeight(root.right); + + return Math.max(left, right) + 1; + } + + /** + * 后续遍历(左右根), 先获取左右子树的高度, 再作判断是否为平衡二叉树 + * 从底部到顶部, 获取每个节点为root根时候的子树最大高度差是否大于1(不平衡), 如果是返回-1; 否则返回当前子树的最大高度 + * + * 时间复杂度为O(n) + * + * https://leetcode-cn.com/problems/balanced-binary-tree/solution/balanced-binary-tree-di-gui-fang-fa-by-jin40789108/ + * + * @return + */ + private static boolean isBalanced2(TreeNode root) { + return depth(root) != -1; + } + + private static int depth(TreeNode root) { + if (root == null) + return 0; + + // 左子树出现不满足条件的 + int left = depth(root.left); + if (left == -1) + return -1; + + // 右子树出现不满足条件的 + int right = depth(root.right); + if (right == -1) + return -1; + + // 检查是否满足条件 + if (Math.abs(left - right) > 1) + return -1; + + // 返回当前左右子树的最大高都 + return Math.max(left, right) + 1; + } +} From 1bbd0f15046532866efe323305429d56e1621313 Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 10 Feb 2020 11:36:24 +0800 Subject: [PATCH 50/74] =?UTF-8?q?=E6=96=B0=E5=A2=9En=E5=8F=89=E6=A0=91?= =?UTF-8?q?=E7=9A=84=E6=9C=80=E5=A4=A7=E6=B7=B1=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/ntree/MaximumDepthOfNAryTree.java | 49 +++++++++++++++++++ src/main/java/com/study/ntree/NTreeNode.java | 21 ++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/main/java/com/study/ntree/MaximumDepthOfNAryTree.java create mode 100644 src/main/java/com/study/ntree/NTreeNode.java diff --git a/src/main/java/com/study/ntree/MaximumDepthOfNAryTree.java b/src/main/java/com/study/ntree/MaximumDepthOfNAryTree.java new file mode 100644 index 0000000..c140a36 --- /dev/null +++ b/src/main/java/com/study/ntree/MaximumDepthOfNAryTree.java @@ -0,0 +1,49 @@ +package com.study.ntree; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * N叉树的最大深度 + * + * 给定一个 N 叉树,找到其最大深度。 + * + * 最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。 + * + * 例如,给定一个 3叉树 : + * 我们应返回其最大深度,3。 + * + * 3 + * / \ \ + * 9 20 11 + * / \ + * 15 7 + * + * 链接:https://leetcode-cn.com/problems/maximum-depth-of-n-ary-tree + */ +public class MaximumDepthOfNAryTree { + + public static void main(String[] args) { + + } + + public static int maxDepth(NTreeNode root) { + // 节点为null是返回0 + if(root == null) + return 0; + + // 没有子节点,返回1(有根节点) + if(root.children.isEmpty()) + return 1; + + List depthList = new ArrayList<>(); + // 获取所有子树的深度 + for(NTreeNode node : root.children){ + depthList.add(maxDepth(node)); + } + + // 返回所有子树的最大深度+1 + return Collections.max(depthList) + 1; + } +} diff --git a/src/main/java/com/study/ntree/NTreeNode.java b/src/main/java/com/study/ntree/NTreeNode.java new file mode 100644 index 0000000..133b47a --- /dev/null +++ b/src/main/java/com/study/ntree/NTreeNode.java @@ -0,0 +1,21 @@ +package com.study.ntree; + +import java.util.List; + +/** + * N叉树, 具有多颗子节点的N叉树 + */ +public class NTreeNode { + + public int val; + public List children; + + public NTreeNode(int val){ + this.val = val; + } + + public NTreeNode(int val, List children){ + this.val = val; + this.children = children; + } +} From cfb22c210c8a6028caad5a3a7afc70726c403b8a Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 10 Feb 2020 13:13:38 +0800 Subject: [PATCH 51/74] =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91=E7=BF=BB?= =?UTF-8?q?=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/binarytree/InvertTree.java | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/main/java/com/study/binarytree/InvertTree.java diff --git a/src/main/java/com/study/binarytree/InvertTree.java b/src/main/java/com/study/binarytree/InvertTree.java new file mode 100644 index 0000000..998cc99 --- /dev/null +++ b/src/main/java/com/study/binarytree/InvertTree.java @@ -0,0 +1,103 @@ +package com.study.binarytree; + +import com.study.utils.TreeUtils; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * 二叉树翻转 + *

+ * 翻转一棵二叉树。 + *

+ * 示例: + *

+ * 输入: + *

+ * 4 + * / \ + * 2 7 + * / \ / \ + * 1 3 6 9 + * 输出: + *

+ * 4 + * / \ + * 7 2 + * / \ / \ + * 9 6 3 1 + *

+ * 链接:https://leetcode-cn.com/problems/invert-binary-tree + */ +public class InvertTree { + + public static void main(String[] args) { + + Integer[] nodes = {4, 2, 7, 1, 3, 6, 9}; + TreeNode root = TreeUtils.buildTree(nodes); + TreeUtils.show(root); + //TreeNode newRoot = invertTree(root); + TreeNode newRoot = invertTree2(root); + TreeUtils.show(newRoot); + } + + + /** + * 后序遍历, 深度优先dfs 自下而上左右节点互换 + *

+ * 时间复杂度O(n) + * + * @param root + * @return + */ + public static TreeNode invertTree(TreeNode root) { + if (root == null) + return null; + + TreeNode right = invertTree(root.right); + TreeNode left = invertTree(root.left); + + // 从最底层叶子节点开始左右互换 + root.left = right; + root.right = left; + + // 返回新的root节点,其左右子节点已经互换 + return root; + } + + /** + * 使用层级遍历的方式 + * + * 时间复杂度O(n) + * @param root + * @return + */ + public static TreeNode invertTree2(TreeNode root) { + if (root == null) + return null; + + Queue queue = new LinkedList(); + + queue.offer(root); + + // 一层一层遍历, 并将左右子节点互换 + while (!queue.isEmpty()) { + int size = queue.size(); + // 当前层级的所有节点取出, 并将其左右子节点进行互换 + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + if (node != null) { + TreeNode tmp = node.left; + node.left = node.right; + node.right = tmp; + + if (node.left != null) + queue.offer(node.left); + if (node.right != null) + queue.offer(node.right); + } + } + } + return root; + } +} From 477b4fae36ee33596052ebc97f04360e9ab54154 Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 10 Feb 2020 13:15:04 +0800 Subject: [PATCH 52/74] =?UTF-8?q?=E9=AA=8C=E8=AF=81=E5=B9=B3=E8=A1=A1?= =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/binarytree/BalancedBinaryTree.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/study/binarytree/BalancedBinaryTree.java b/src/main/java/com/study/binarytree/BalancedBinaryTree.java index 4e47458..4961c5d 100644 --- a/src/main/java/com/study/binarytree/BalancedBinaryTree.java +++ b/src/main/java/com/study/binarytree/BalancedBinaryTree.java @@ -3,7 +3,7 @@ import com.study.utils.TreeUtils; /** - * 平衡二叉树 + * 验证平衡二叉树 * * 给定一个二叉树,判断它是否是高度平衡的二叉树。 *

@@ -45,7 +45,8 @@ public static void main(String[] args) { TreeNode root = TreeUtils.buildTree(nodes); TreeUtils.show(root); - System.out.println(isBalanced(root)); + //System.out.println(isBalanced(root)); + System.out.println(isBalanced2(root)); } /** @@ -114,6 +115,11 @@ private static boolean isBalanced2(TreeNode root) { return depth(root) != -1; } + /** + * 后续遍历 + * @param root + * @return + */ private static int depth(TreeNode root) { if (root == null) return 0; From dc4953fa9c950984b150d9665904aeef2928d1cf Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 10 Feb 2020 13:27:43 +0800 Subject: [PATCH 53/74] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/array/MaximumSubarray.java | 2 +- .../com/study/binarytree/MinimumDepth.java | 12 +-- .../LowestCommonAncestor.java | 7 +- .../ValidateBinarySearchTree.java | 80 ++++++++++++++----- .../LongestCommonSubsequence.java | 2 + .../MaximumProductSubarray.java | 3 +- src/main/java/com/study/search/BFS.java | 5 +- src/main/java/com/study/search/DFS.java | 8 +- src/main/java/com/study/trietree/Trie.java | 12 +-- src/main/java/com/study/utils/TreeUtils.java | 2 +- 10 files changed, 87 insertions(+), 46 deletions(-) diff --git a/src/main/java/com/study/array/MaximumSubarray.java b/src/main/java/com/study/array/MaximumSubarray.java index ef6c9c0..8f07c52 100644 --- a/src/main/java/com/study/array/MaximumSubarray.java +++ b/src/main/java/com/study/array/MaximumSubarray.java @@ -9,7 +9,7 @@ *

* 输入: [-2,1,-3,4,-1,2,1,-5,4], * 输出: 6 - * 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 + * 解释: 连续子数组 [4,-1,2,1] 的和最大,为6。 * 进阶: *

* 如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。 diff --git a/src/main/java/com/study/binarytree/MinimumDepth.java b/src/main/java/com/study/binarytree/MinimumDepth.java index 471713b..f1d7b75 100644 --- a/src/main/java/com/study/binarytree/MinimumDepth.java +++ b/src/main/java/com/study/binarytree/MinimumDepth.java @@ -34,13 +34,15 @@ public class MinimumDepth { public static void main(String[] args) { Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; TreeNode root = TreeUtils.buildTree(arr); - + TreeUtils.show(root); //System.out.println(minDepthByRecursion(root)); System.out.println(minDepthByLoop(root)); } /** - * 使用分治的方法 从左右节点进行递归 + * 使用分治的方法 从左右节点进行递归, + * 注意: 当root节点左右孩子都为空时,返回1 + * * 时间复杂度为O(n) * * @param root @@ -53,13 +55,13 @@ private static int minDepthByRecursion(TreeNode root) { int left = minDepthByRecursion(root.left); int right = minDepthByRecursion(root.right); - // 如果左边层级为0, 那么返回右边层级+1 + // 如果左边子节点为空的时候, 那么返回右边不为空的孩子深度+1 if (left == 0) return right + 1; - // 如果右边层级为0, 返回左边层级+1 + // 如果右边子节点为空的时候, 那么返回左边不为空的孩子深度+1 else if (right == 0) return left + 1; - else //如果左右层级都不为0, 返回左右层数最小值+1 + else //如果左右子节点都不为空, 返回左右深度最小的那个+1 return Math.min(left, right) + 1; } diff --git a/src/main/java/com/study/binarytree/binarysearchtree/LowestCommonAncestor.java b/src/main/java/com/study/binarytree/binarysearchtree/LowestCommonAncestor.java index f3a2e90..52412c5 100644 --- a/src/main/java/com/study/binarytree/binarysearchtree/LowestCommonAncestor.java +++ b/src/main/java/com/study/binarytree/binarysearchtree/LowestCommonAncestor.java @@ -6,7 +6,7 @@ import java.util.List; /** - * 二叉搜索树的最近公共祖先 + * 二叉搜索树的最近公共祖先 lowest common ancestor *

* 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 *

@@ -39,9 +39,10 @@ public static void main(String[] args) { // 由于二叉搜索树也是二叉树, 所以既可以使用二叉搜索树的方法也可以使用二叉树的方法求最近公共祖先 int[] arr = {5, 3, 7, 1, 4, 6, 8}; List treeNodes = TreeUtils.buildTreeAndList(arr); - TreeNode root = treeNodes.get(0); + TreeUtils.show(root); + TreeNode p = treeNodes.get(5); TreeNode q = treeNodes.get(6); @@ -49,8 +50,6 @@ public static void main(String[] args) { TreeNode lca = getlca2(root, p, q); if (lca != null) System.out.println(lca.val); - - } /** diff --git a/src/main/java/com/study/binarytree/binarysearchtree/ValidateBinarySearchTree.java b/src/main/java/com/study/binarytree/binarysearchtree/ValidateBinarySearchTree.java index d862775..9de90c9 100644 --- a/src/main/java/com/study/binarytree/binarysearchtree/ValidateBinarySearchTree.java +++ b/src/main/java/com/study/binarytree/binarysearchtree/ValidateBinarySearchTree.java @@ -53,6 +53,9 @@ public static void main(String[] args) { //Integer[] arr = {1, 1}; Integer[] arr = {5, 1, 6, null, null, 3, 7}; + + //Integer[] arr = {5, 1, 9, null, null, 7, 10, null, null, null, null, 8, 3}; + //Integer[] arr = {3, 9, 20, null, null, 15, 7}; TreeNode root = TreeUtils.buildTree(arr); @@ -75,8 +78,8 @@ public static void main(String[] args) { //System.out.println(isValidBST_InOrder_2(root)); // 递归的方式判断是否所有子树 都满足 左子树<节点<右子树 - System.out.println(isValidBST(root, null, null)); - //System.out.println(isValidBST_2(root, null, null)); + System.out.println(isValidBST(root)); + //System.out.println(isValidBST_2(root)); //System.out.println(isValidBST2(root)); } @@ -122,45 +125,57 @@ private static boolean isValidBST_InOrder_2(TreeNode root) { } /** - * 验证是否为二叉搜索树, 可以判断左边所有与节点值是否小于根节点, 右边所有节点值 大于 根节点. + * 验证是否为二叉搜索树, 需要判断左边所有与节点值是否小于根节点, 右边所有节点值 大于 根节点. * 以及每棵子树 都是 左子树 < 节点 < 右子树 *

- * 深度优先搜索: 使用递归的方式遍历左右子树, 判断是否每个节点 都满足 左子树 < 节点 < 右子树 + * 使用深度优先搜索: 需要把最小值min和最大值max传入, 让左子树所有节点与根节点值min作比较, 让右子树所有节点与根节点值max作比较. + * 对于根节点左边所有子树, 最大值为根节点max; 对于根节点右边所有子树,最小值为根节点min; + * *

* 节点的左子树只包含小于当前节点的数。 * 节点的右子树只包含大于当前节点的数。 * 所有左子树和右子树自身必须也是二叉搜索树。 * + * 时间复杂度 : O(N)。每个结点访问一次。 + * 空间复杂度 : O(N)。我们跟进了整棵树。 + * * @param root * @return */ - private static boolean isValidBST(TreeNode root, Integer min, Integer max) { + private static boolean isValidBST(TreeNode root){ + return helper(root,null,null); + } + + private static boolean helper(TreeNode root, Integer min, Integer max) { if (root == null) //如果root为空 说明当前节点已经递归到最底层了 或者树本身就是空树 return true; - if (min != null && root.val < min) //如果root小于最小值, 不是二叉搜索树 + //如果root小于最小值, 不是二叉搜索树 + if (min != null && root.val < min) { + System.out.println(String.format("不满足的节点: node: %d, min: %d", root.val, min)); return false; - if (max != null && root.val > max) //如果root大于最大值, 也不是二叉搜索树 + } + + //如果root大于最大值, 也不是二叉搜索树 + if (max != null && root.val > max) { + System.out.println(String.format("不满足的节点: node: %d, max: %d", root.val, max)); return false; + } - System.out.print(root.val + " "); + //System.out.print(root.val + " "); // 是否左子树的所有节点肯定都比根节点小 右子树的所有节点都比根大 - boolean left = isValidBST(root.left, min, root.val); - boolean right = isValidBST(root.right, root.val, max); - - if (!left) { - System.out.println(String.format("不满足的左子树节点: node: %d, min: %d", root ==null || root.left == null || root.left.val == null ? "空节点" : root.left.val, min)); - } - - if(!right){ - System.out.println(String.format("不满足的右子树节点: node: %d, max: %d", root ==null || root.right == null || root.right.val == null ? "空节点" : root.left.val, max)); - } + boolean left = helper(root.left, min, root.val); + boolean right = helper(root.right, root.val, max); return left && right; //如果左边和右边都满足条件, 则说明该数为二叉搜索树 //return isValidBST(root.left, min, root.val) && isValidBST(root.right, root.val, max); } - private static boolean isValidBST_2(TreeNode root, Integer min, Integer max) { + private static boolean isValidBST_2(TreeNode root){ + return helper2(root,null,null); + } + + private static boolean helper2(TreeNode root, Integer min, Integer max) { if (root == null) return true; if (min != null && root.val < min) //跟节点不能小于最小值 @@ -168,9 +183,34 @@ private static boolean isValidBST_2(TreeNode root, Integer min, Integer max) { if (max != null && root.val > max) //跟节点也不能大于最大值 return false; - return isValidBST_2(root.left, min, root.val) && isValidBST_2(root.right, root.val, max); + return helper2(root.left, min, root.val) && helper2(root.right, root.val, max); } + /* 不对的做法, 这只判断了每个子树 是否满足 左子节点 < 节点 < 右子节点, 没有需要判断 左边子树所有节点 < 根节点 < 右边子树所有节点 */ +// /** +// * 验证是否为二叉搜索树, 需要判断左边所有与节点值是否小于根节点, 右边所有节点值 大于 根节点. +// * 不传入根节点值, 但是需要判断值左右节点值是否为空 +// * +// * @param root +// * @return +// */ +// private static boolean isValidBST2(TreeNode root) { +// if (root == null) +// return true; +// +// if (root.left != null && root.left.val != null && root.val < root.left.val) { +// System.out.println(String.format("不满足的左节点: node: %d, root: %d", root.left.val, root.val)); +// return false; +// } +// +// if (root.right != null && root.right.val != null && root.val > root.right.val) { +// System.out.println(String.format("不满足右的节点: node: %d, root: %d", root.right.val, root.val)); +// return false; +// } +// +// return isValidBST2(root.left) && isValidBST2(root.right); +// } + /** * 使用中序遍历 获取二叉树每个节点 *

diff --git a/src/main/java/com/study/dynamicprogramming/LongestCommonSubsequence.java b/src/main/java/com/study/dynamicprogramming/LongestCommonSubsequence.java index aa83446..58ddb60 100644 --- a/src/main/java/com/study/dynamicprogramming/LongestCommonSubsequence.java +++ b/src/main/java/com/study/dynamicprogramming/LongestCommonSubsequence.java @@ -53,6 +53,8 @@ public static void main(String[] args) { * i=0或者j=0 LCS(i,j)=0 //如果两个字符中有有一个为空, 那LCS=0 * i>0且j>0 且Xi=Yj LCS(i,j)=LCS(i-1,j-1) + 1 // 如果两个字符相等, 那么LCS为其相邻左斜方值+1 * i>0且j>0 且Xi!=Yj LCS(i,j)=MAX{(LCS(i-1,j),LCS(i,j-1)} //如果连个字符不相等, 那么LCS为其相邻的左和上方值中最大的一个 + * 就这样不断的将二维数组上各个位置上的值填满. + * 最后取数组右下角最后一个值为lcs * * @param text1 * @param text2 diff --git a/src/main/java/com/study/dynamicprogramming/MaximumProductSubarray.java b/src/main/java/com/study/dynamicprogramming/MaximumProductSubarray.java index 7c2e666..e9f6bc4 100644 --- a/src/main/java/com/study/dynamicprogramming/MaximumProductSubarray.java +++ b/src/main/java/com/study/dynamicprogramming/MaximumProductSubarray.java @@ -21,9 +21,10 @@ public class MaximumProductSubarray { public static void main(String[] args) { - int[] nums = {2, 3, -2, 4}; + //int[] nums = {2, 3, -2, 4}; //int[] nums = {-2, 0, -1}; //int[] nums = {0, 2}; + int[] nums = {2, -5, -2, -3}; System.out.println(maxProduct(nums)); System.out.println(maxProduct2(nums)); } diff --git a/src/main/java/com/study/search/BFS.java b/src/main/java/com/study/search/BFS.java index 114768a..1c56c7c 100644 --- a/src/main/java/com/study/search/BFS.java +++ b/src/main/java/com/study/search/BFS.java @@ -17,9 +17,8 @@ public class BFS { public static void main(String[] args) { - int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - List treeNodes = TreeUtils.buildTree(arr); - TreeNode root = treeNodes.get(0); + Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + TreeNode root = TreeUtils.buildTree(arr); List> visitedNodes = searchByLoop(root); // List> visitedNodes = searchByRecursion(root); diff --git a/src/main/java/com/study/search/DFS.java b/src/main/java/com/study/search/DFS.java index fd0a4df..ea7fe67 100644 --- a/src/main/java/com/study/search/DFS.java +++ b/src/main/java/com/study/search/DFS.java @@ -16,15 +16,13 @@ public class DFS { /** - * 深度优先遍历 和 二叉树的前序遍历(根 左 右)是一样的 + * 深度优先遍历 * * @param args */ public static void main(String[] args) { - int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - List treeNodes = TreeUtils.buildTree(arr); - TreeNode root = treeNodes.get(0); - + Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + TreeNode root = TreeUtils.buildTree(arr); List nodes = search(root); for (TreeNode node : nodes) { System.out.print(node.val + " "); diff --git a/src/main/java/com/study/trietree/Trie.java b/src/main/java/com/study/trietree/Trie.java index 3370b3c..6c8d52b 100644 --- a/src/main/java/com/study/trietree/Trie.java +++ b/src/main/java/com/study/trietree/Trie.java @@ -13,7 +13,7 @@ *

* 优点 * 利用字符串的公共前缀来减少查询时间,最大限度的减少无谓的字符串比较,查询效率比哈希树高。 - * + *

* 遍历算法: bfs深度优先遍历 先根再子节点 * *

@@ -33,7 +33,7 @@ public static void main(String[] args) { } System.out.println("------打印字典树中所有单词--------"); - printAllWords(root , "#"); + printAllWords(root, "#"); System.out.println("--------------------------------"); // 是否包含这个单词 @@ -82,7 +82,7 @@ public static void insert(String str, TrieNode root) { } /** - * 计算单词前缀的数量 + * 计算拥有该前缀的单词 * * @param prefix * @return @@ -105,7 +105,7 @@ public static int countPrefix(String prefix, TrieNode root) { } /** - * 打印指定前缀的单词 + * 找出并打印出拥有指定前缀的所有单词 * * @param prefix * @return @@ -172,7 +172,7 @@ public static boolean containsWord(String str, TrieNode root) { /** * 打印字典树里面所有字符char - * + *

* 前序遍历字典树. bfs深度优先遍历 * *

@@ -213,7 +213,7 @@ public static void preTraverse2(TrieNode root) { * 打印字典树里所有的单词 * 将递归走的每个char进行拼接,直到单词尾部,然后输出这个单词 * - * @param root 根节点 + * @param root 根节点 * @param prefix 前缀 */ private static void printAllWords(TrieNode root, String prefix) { diff --git a/src/main/java/com/study/utils/TreeUtils.java b/src/main/java/com/study/utils/TreeUtils.java index b3273d0..e2c7849 100644 --- a/src/main/java/com/study/utils/TreeUtils.java +++ b/src/main/java/com/study/utils/TreeUtils.java @@ -75,7 +75,7 @@ public static TreeNode buildTree(Integer[] nodes) { // 用于获得树的层数 - public static int getTreeDepth(TreeNode root) { + private static int getTreeDepth(TreeNode root) { return root == null ? 0 : (1 + Math.max(getTreeDepth(root.left), getTreeDepth(root.right))); } From b71eb258b0c79a0f96f805edc96187d0651d5d9b Mon Sep 17 00:00:00 2001 From: longwu Date: Tue, 11 Feb 2020 13:33:14 +0800 Subject: [PATCH 54/74] =?UTF-8?q?=E6=89=BE=E5=87=BA=E6=9C=89=E5=BA=8F?= =?UTF-8?q?=E6=95=B0=E7=BB=84=E4=B8=AD=E7=9A=84=E6=9C=80=E5=B0=8F=E7=BB=9D?= =?UTF-8?q?=E5=AF=B9=E5=80=BC=E5=85=83=E7=B4=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/study/search/MinAbs.java | 65 ++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/main/java/com/study/search/MinAbs.java diff --git a/src/main/java/com/study/search/MinAbs.java b/src/main/java/com/study/search/MinAbs.java new file mode 100644 index 0000000..8c8382d --- /dev/null +++ b/src/main/java/com/study/search/MinAbs.java @@ -0,0 +1,65 @@ +package com.study.search; + +/** + * 找出有序数组中绝对值最小的元素 + */ +public class MinAbs { + + public static void main(String[] args) { + // 一个升序整形数组, 找到绝对值最小的输出 + + //int[] arr = {-3,-2,-1,5}; + //int[] arr = {-3, -2, -1, 0, 3, 5}; + int[] arr = {-1, 1, 3, 5}; + + //int[] arr = {1, 3, 5}; + //int[] arr = {-9, -6, - 2, 0}; + System.out.println(min(arr)); + } + + /** + * 利用二分查找进行,使得时间复杂度为O(logN), 低于遍历的O(N) + * @param arr + * @return + */ + private static int min(int[] arr) { + int left = 0; + int right = arr.length - 1; + + if (arr[left] >= 0) + return arr[left]; + if (arr[right] <= 0) + return arr[right]; + + while (left <= right) { + int middle = (left + right) / 2; + // 大于0往左找最近的负数 + if (arr[middle] > 0) { + // 判断下一个数是否为负数 + if (arr[middle - 1] < 0) { + // 返回正负交届的绝对值最小的那个数 + if (Math.abs(arr[middle]) > Math.abs(arr[middle - 1])) + return arr[middle - 1]; + else + return arr[middle]; + } else + right = middle; + // 小于0往右找最近的正数 + } else if (arr[middle] < 0) { + // 判断下一个数是否为正数 + if (arr[middle + 1] > 0) + // 返回正负交届的绝对值最小的那个数 + if (Math.abs(arr[middle]) > Math.abs(arr[middle + 1])) + return arr[middle + 1]; + else + return arr[middle]; + else + left = middle; + } else { + // 如果为0,直接返回 (0是所有正数中绝对值最小的) + return arr[middle]; + } + } + return 0; + } +} From aea9ab1859a5280e8cb64f40bd4e8d2a94984c80 Mon Sep 17 00:00:00 2001 From: longwu Date: Tue, 11 Feb 2020 23:31:30 +0800 Subject: [PATCH 55/74] =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91=E9=81=8D?= =?UTF-8?q?=E5=8E=862?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/study/binarytree/LevelTraversal2.java | 82 ++++++++++++++++++ .../java/com/study/binarytree/TreeNode2.java | 11 +++ src/main/java/com/study/utils/TreeUtils.java | 86 +++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 src/main/java/com/study/binarytree/LevelTraversal2.java create mode 100644 src/main/java/com/study/binarytree/TreeNode2.java diff --git a/src/main/java/com/study/binarytree/LevelTraversal2.java b/src/main/java/com/study/binarytree/LevelTraversal2.java new file mode 100644 index 0000000..ba534fb --- /dev/null +++ b/src/main/java/com/study/binarytree/LevelTraversal2.java @@ -0,0 +1,82 @@ +package com.study.binarytree; + + +import com.study.utils.TreeUtils; +import sun.reflect.generics.tree.Tree; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + + +/** + * 二叉树遍历2 + * + * 将一个二叉树各节点输出, 奇数行按顺序输出,偶数行倒叙输出 + * input: + * A + * / \ + * B C + * / \ \ + * D E F + * / \ + * G H + * output: + * A + * CB + * DEF + * HG + */ +public class LevelTraversal2 { + + public static void main(String[] args) { + String[] nodes = {"A", "B", "C", "D", "E", null, "F", null,null, "G", null, null, null, null, "H"}; + TreeNode2 root = TreeUtils.buildTree(nodes); + TreeUtils.show(root); + List> list = getTreeList(root); + + int level = 1; + for (List levelList : list) { + // 奇数行 正序输出 + if (level % 2 != 0) { + for (int i = 0; i < levelList.size(); i++) { + System.out.print(levelList.get(i)); + } + } else { + //偶数行 倒叙输出 + for (int i = levelList.size() - 1; i >= 0; i--) { + System.out.print(levelList.get(i)); + } + } + System.out.println(); + level++; + } + } + + private static List> getTreeList(TreeNode2 root) { + List> list = new ArrayList<>(); + + Queue queue = new LinkedList<>(); + queue.offer(root); + + while (!queue.isEmpty()) { + List levelList = new ArrayList<>(); + int size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode2 node = queue.poll(); + levelList.add(node.val); + + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + list.add(levelList); + } + + return list; + } +} diff --git a/src/main/java/com/study/binarytree/TreeNode2.java b/src/main/java/com/study/binarytree/TreeNode2.java new file mode 100644 index 0000000..4321cb0 --- /dev/null +++ b/src/main/java/com/study/binarytree/TreeNode2.java @@ -0,0 +1,11 @@ +package com.study.binarytree; + +public class TreeNode2 { + public TreeNode2 left; + public TreeNode2 right; + public String val; + + public TreeNode2(String val) { + this.val = val; + } +} diff --git a/src/main/java/com/study/utils/TreeUtils.java b/src/main/java/com/study/utils/TreeUtils.java index e2c7849..f36ebf6 100644 --- a/src/main/java/com/study/utils/TreeUtils.java +++ b/src/main/java/com/study/utils/TreeUtils.java @@ -1,6 +1,7 @@ package com.study.utils; import com.study.binarytree.TreeNode; +import com.study.binarytree.TreeNode2; import java.util.ArrayList; import java.util.List; @@ -72,6 +73,25 @@ public static TreeNode buildTree(Integer[] nodes) { return treeNodes.get(0); } + public static TreeNode2 buildTree(String[] nodes) { + List treeNodes = new ArrayList(); + for (String data : nodes) { + treeNodes.add(new TreeNode2(data)); + } + //创建一个二叉树 + //创建其他节点 每个节点的左子节点都是这个节点的2倍+1, 右子节点都是这个节点的2倍+2 + for (int i = 0; i < nodes.length / 2; i++) { + if (treeNodes.get(i * 2 + 1).val != null) { + treeNodes.get(i).left = treeNodes.get(i * 2 + 1); + } + if (i * 2 + 2 < treeNodes.size()) { + if (treeNodes.get(i * 2 + 2).val != null) + treeNodes.get(i).right = treeNodes.get(i * 2 + 2); + } + } + return treeNodes.get(0); + } + // 用于获得树的层数 @@ -79,6 +99,10 @@ private static int getTreeDepth(TreeNode root) { return root == null ? 0 : (1 + Math.max(getTreeDepth(root.left), getTreeDepth(root.right))); } + private static int getTreeDepth(TreeNode2 root) { + return root == null ? 0 : (1 + Math.max(getTreeDepth(root.left), getTreeDepth(root.right))); + } + private static void writeArray(TreeNode currNode, int rowIndex, int columnIndex, String[][] res, int treeDepth) { // 保证输入的树不为空 @@ -106,6 +130,32 @@ private static void writeArray(TreeNode currNode, int rowIndex, int columnIndex, } } + private static void writeArray(TreeNode2 currNode, int rowIndex, int columnIndex, String[][] res, int treeDepth) { + // 保证输入的树不为空 + if (currNode == null) return; + // 先将当前节点保存到二维数组中 + res[rowIndex][columnIndex] = String.valueOf(currNode.val); + + // 计算当前位于树的第几层 + int currLevel = ((rowIndex + 1) / 2); + // 若到了最后一层,则返回 + if (currLevel == treeDepth) return; + // 计算当前行到下一行,每个元素之间的间隔(下一行的列索引与当前元素的列索引之间的间隔) + int gap = treeDepth - currLevel - 1; + + // 对左儿子进行判断,若有左儿子,则记录相应的"/"与左儿子的值 + if (currNode.left != null) { + res[rowIndex + 1][columnIndex - gap] = "/"; + writeArray(currNode.left, rowIndex + 2, columnIndex - gap * 2, res, treeDepth); + } + + // 对右儿子进行判断,若有右儿子,则记录相应的"\"与右儿子的值 + if (currNode.right != null) { + res[rowIndex + 1][columnIndex + gap] = "\\"; + writeArray(currNode.right, rowIndex + 2, columnIndex + gap * 2, res, treeDepth); + } + } + /** * 打印出二叉树的结构 * @@ -149,4 +199,40 @@ public static void show(TreeNode root) { System.out.println(sb.toString()); } } + + + public static void show(TreeNode2 root) { + if (root == null) System.out.println("EMPTY!"); + // 得到树的深度 + int treeDepth = getTreeDepth(root); + + // 最后一行的宽度为2的(n - 1)次方乘3,再加1 + // 作为整个二维数组的宽度 + int arrayHeight = treeDepth * 2 - 1; + int arrayWidth = (2 << (treeDepth - 2)) * 3 + 1; + // 用一个字符串数组来存储每个位置应显示的元素 + String[][] res = new String[arrayHeight][arrayWidth]; + // 对数组进行初始化,默认为一个空格 + for (int i = 0; i < arrayHeight; i ++) { + for (int j = 0; j < arrayWidth; j ++) { + res[i][j] = " "; + } + } + + // 从根节点开始,递归处理整个树 + // res[0][(arrayWidth + 1)/ 2] = (char)(root.val + '0'); + writeArray(root, 0, arrayWidth/ 2, res, treeDepth); + + // 此时,已经将所有需要显示的元素储存到了二维数组中,将其拼接并打印即可 + for (String[] line: res) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < line.length; i ++) { + sb.append(line[i]); + if (line[i].length() > 1 && i <= line.length - 1) { + i += line[i].length() > 4 ? 2: line[i].length() - 1; + } + } + System.out.println(sb.toString()); + } + } } From 360c7ec5e1ba878bdfb855a5feec44be56ce1c0c Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 17 Feb 2020 23:49:32 +0800 Subject: [PATCH 56/74] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AE=97=E6=B3=95:=20?= =?UTF-8?q?=E6=89=BE=E5=88=B0=E9=93=BE=E8=A1=A8=E4=B8=AD=E9=97=B4=E7=9A=84?= =?UTF-8?q?=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/linkedlist/DoubleLinkedList.java | 177 ++++++++++++++++++ .../com/study/linkedlist/FindMiddleOne.java | 157 ++++++++++++++++ .../java/com/study/utils/LinkedListUtils.java | 46 +++++ 3 files changed, 380 insertions(+) create mode 100644 src/main/java/com/study/linkedlist/DoubleLinkedList.java create mode 100644 src/main/java/com/study/linkedlist/FindMiddleOne.java create mode 100644 src/main/java/com/study/utils/LinkedListUtils.java diff --git a/src/main/java/com/study/linkedlist/DoubleLinkedList.java b/src/main/java/com/study/linkedlist/DoubleLinkedList.java new file mode 100644 index 0000000..fa4538b --- /dev/null +++ b/src/main/java/com/study/linkedlist/DoubleLinkedList.java @@ -0,0 +1,177 @@ +package com.study.linkedlist; + +import com.study.utils.LinkedListUtils; + +/** + * 双向升序链表 + *

+ * 每个节点都有直接前驱后直接后继, 除了第一个节点没有直接前驱和最后一个节点没有直接后继 + * 结构如下 + *

+ * 1 -> 2 -> 4 -> 5 -> 6 -> null + * null <- 1 <- 2 <- 4 <- 5 <- 6 + */ +public class DoubleLinkedList { + + public static void main(String[] args) { + //往 1 -> 2 -> 4 -> 5 -> 6 中插入 一个0或3 + // <- <- <- <- + DoubleLinkedNode node1 = new DoubleLinkedNode(1); + DoubleLinkedNode node2 = new DoubleLinkedNode(2); + DoubleLinkedNode node3 = new DoubleLinkedNode(3); + DoubleLinkedNode node4 = new DoubleLinkedNode(4); + DoubleLinkedNode node5 = new DoubleLinkedNode(5); + DoubleLinkedNode node6 = new DoubleLinkedNode(6); + + node1.next = node2; + node2.prev = node1; + node2.next = node3; + node3.prev = node2; + node3.next = node4; + node4.prev = node3; + node4.next = node5; + node5.prev = node4; + node5.next = node6; + node6.prev = node5; + +// printLinkedList(node1); +// +// Node head1 = insertNode(node1, 0); +// +// printLinkedList(head1); +// System.out.println(); +// +// Node head2 = insertNode(head1, 8); +// +// printLinkedList(head2); +// System.out.println(); +// Node head3 = insertNode(head2, 4); +// +// printLinkedList(head3); + + System.out.println("---------------------"); + DoubleLinkedNode head4 = deleteNode(node1, 3); + LinkedListUtils.printLinkedList(head4); + +// System.out.println(); +// head4 = deleteNode(head4, 0); +// printLinkedList(head4); +// +// System.out.println(); +// head4 = deleteNode(head4, 5); +// printLinkedList(head4); +// +// System.out.println(); +// head4 = deleteNode(head4, 8); +// printLinkedList(head4); + } + + + /** + * 插入一个新value并返回插入后的头节点 + *

+ * 新插入的节点可能是第一个,也可能是中间或者最后一个 + * + * @param head + * @param value + * @return + */ + private static DoubleLinkedNode insertNode(DoubleLinkedNode head, int value) { + DoubleLinkedNode newNode = new DoubleLinkedNode(value); + DoubleLinkedNode originalHead = head; + while (head != null) { + // 如果插入的节点比链表里的每个节点都小 + if (newNode.val < head.val) { + // 头节点 + // 如果head是头节点, 将新节点插在头节点的前面 + if (head.prev == null) { + newNode.next = head; + head.prev = newNode; + return newNode; + } else { + // 中间节点 + //如果当前head不是头节点,插入新节点的时候需要修改上一个节点的next,当前节点的pre和next, 以及下一个节点的pre, 4个指针 + //修改当前节点的上一个节点, 将上一个节点存一个临时变量 + DoubleLinkedNode prevNode = head.prev; + //将上一个节点的临时变量的后继指针指向新节点, 将新节点的前驱指针指向上一个节点的临时变量 + prevNode.next = newNode; + newNode.prev = prevNode; + // 将当前节点的前驱指向新节点, 新节点的后继指向当前节点 + head.prev = newNode; + newNode.next = head; + return originalHead; + } + } + // 末尾节点 + //如果新插入的节点比链表最后一个还要大 + if (head.next == null) { + //修改当前节点的后继和新节点的前驱 + head.next = newNode; + newNode.prev = head; + return originalHead; + } + head = head.next; + } + return originalHead; + } + + + /** + * 删除一个值对应的节点,并返回新的头节点 + *

+ * 该值对应的节点可能不存在, 也可能是头,中,尾部位的节点 + * + * @param head + * @param value + */ + private static DoubleLinkedNode deleteNode(DoubleLinkedNode head, int value) { + System.out.println("要删除的节点是" + value); + DoubleLinkedNode deleteNode = new DoubleLinkedNode(value); + DoubleLinkedNode originalHead = head; + while (head != null) { + // 如果要删除的节点存在 + if (head.val == deleteNode.val) { + // 如果删除的节点是头节点, 将头节点的next和下一个节点prev改为null + if (head.prev == null) { + DoubleLinkedNode secondNode = head.next; + secondNode.prev = null; + head.next = null; //这行代码也可以不写 因为我们返回的链表secondNode里面已经没有了head,所以head.next也可以不处理 + System.out.println("删除的节点为头节点"); + return secondNode; //返回新的头结点,第二个节点 + } else { + //删除的节点是中间节点和尾节点 + //如果删除的节点是尾节点 + if (head.next == null) { + // 将尾节点的prev和上一个节点的next改为null + DoubleLinkedNode prev = head.prev; + prev.next = null; + head.prev = null; //这行代码也可以不写 + System.out.println("删除的节点为尾节点"); + + } else { //如果删除的节点为中间的一个节点 + //将要删除的节点的上一个节点next改为要删除节点的下一个节点, 将要删除节点的下一个节点的prev改为它的上一个节点 + + //将当前节点的上一个和下一个节点拿出来存临时变量 + DoubleLinkedNode prev = head.prev; + DoubleLinkedNode next = head.next; + //将当前节点的前驱和后继改为null + //head.prev = head.next = null; //这行代码也可以不写 + //将上一个节点的next指向下一个节点 + prev.next = next; + //将下一个节点的prev指向上一个节点 + next.prev = prev; + + System.out.println("删除的节点为中间节点"); + } + return originalHead; //返回原来的头结点 + } + } + head = head.next; + } + System.out.println("要删除的节点不存在"); + // 如果删除的节点不存在 + return originalHead; + } + + +} \ No newline at end of file diff --git a/src/main/java/com/study/linkedlist/FindMiddleOne.java b/src/main/java/com/study/linkedlist/FindMiddleOne.java new file mode 100644 index 0000000..8c94759 --- /dev/null +++ b/src/main/java/com/study/linkedlist/FindMiddleOne.java @@ -0,0 +1,157 @@ +package com.study.linkedlist; + +import com.study.utils.LinkedListUtils; + +/** + * 找到链表中间的元素, + *

+ * 对于单链表可以使用使用快慢双指针,快指针走完,慢指针所在的位置即是中间. + * 对于双链表,可以使用快慢双指针, 如果知道头尾两个节点, 可以使用前后两个指针以相同的速度往中间走. + * + *

+ * https://blog.csdn.net/lihui930310560/article/details/53319367/ + */ +public class FindMiddleOne { + + public static void main(String[] args) { + + //int length = 8; + int length = 9; + + // 创建一个长度为20的 单向链表 + LinkedNode head = new LinkedNode(1); + LinkedNode cur = head; + + int i = 2; + while (i <= length) { + cur.next = new LinkedNode(i); + cur = cur.next; + i++; + } + + LinkedListUtils.printLinkedList(head); + + // 找出单向链表中间的元素 + System.out.println(findMiddle(head)); + + //创建一个双向链表 + DoubleLinkedNode head2 = new DoubleLinkedNode(1); + + DoubleLinkedNode cur2 = head2; + i = 2; + while (i <= length) { + DoubleLinkedNode nextNode = new DoubleLinkedNode(i); + cur2.next = nextNode; + nextNode.prev = cur2; + + cur2 = cur2.next; + i++; + } + + LinkedListUtils.printLinkedList(head2); + // 找出双向链表中间的元素 + System.out.println(findMiddle(head2)); + + // 通过头尾节点找出中间元素 + System.out.println(findMiddle(head2, cur2)); + } + + /** + * 找到单向链表的中间节点 + * 可以使用一个快慢指针, 一个走一步,一个走两步 + * + * @param head + */ + private static int findMiddle(LinkedNode head) { + if (head == null) + return -1; + + // 默认都已经走了1步 + LinkedNode slow = head; + LinkedNode fast = head; + + while (fast != null && slow != null && fast.next != null) { + + slow = slow.next; + fast = fast.next.next; + + // 如果快指针刚好走完或者差半步走完,结束旅程 + if (fast.next == null || fast.next.next == null) + break; + } + + // 由于大家都是从第一个节点开始 1 + 2 * n + // 如果快指针的下一个节点为空,说明链表长度为奇数,则当前慢指针的值为中间值 + if (fast.next == null) + return slow.val; + + // 如果快指针差一步走完, 链表长度为偶数, 则当前慢指针的值也是中间值 + if (fast.next.next == null) + return slow.val; + + return -1; + } + + /** + * 找到双向链表中间的元素, 使用快慢双指针 + * + * @param head + * @return + */ + private static int findMiddle(DoubleLinkedNode head) { + if (head == null) + return -1; + + // 默认都已经走了1步 + DoubleLinkedNode slow = head; + DoubleLinkedNode fast = head; + + while (fast != null && slow != null && fast.next != null) { + + slow = slow.next; + fast = fast.next.next; + + // 如果快指针刚好走完或者差半步走完,结束旅程 + if (fast.next == null || fast.next.next == null) + break; + } + + // 由于大家都是从第一个节点开始 1 + 2 * n + // 如果快指针的下一个节点为空,说明链表长度为奇数,则当前慢指针的值为中间值 + if (fast.next == null) + return slow.val; + + // 如果快指针差一步走完, 链表长度为偶数, 则当前慢指针的值也是中间值 + if (fast.next.next == null) + return slow.val; + + return -1; + } + + /** + * 使用前后双指针, 一个从头开始走, 一个从尾开始走, 走到相碰的地方就是 + * + * @param head + * @return + */ + private static int findMiddle(DoubleLinkedNode head, DoubleLinkedNode tail) { + + DoubleLinkedNode newHead = head; + DoubleLinkedNode newTail = tail; + + int middle = -1; + + while (newHead != null && newTail != null) { + + // 链表长度为奇数的情况下,两个指针会走在同一个节点上,则该节点就是中间节点 + // 链表长度为偶数的情况下,头指针所在的节点为中间点 + if (newHead == newTail || newHead.next == newTail) + middle = newHead.val; + + newHead = newHead.next;// 头节点往后走 + newTail = newTail.prev;// 尾节点往前走 + } + + return middle; + } +} diff --git a/src/main/java/com/study/utils/LinkedListUtils.java b/src/main/java/com/study/utils/LinkedListUtils.java new file mode 100644 index 0000000..065b4f7 --- /dev/null +++ b/src/main/java/com/study/utils/LinkedListUtils.java @@ -0,0 +1,46 @@ +package com.study.utils; + +import com.study.linkedlist.DoubleLinkedNode; +import com.study.linkedlist.LinkedNode; + +public final class LinkedListUtils { + + public static void printLinkedList(LinkedNode head) { + while (head != null) { + if (head.next == null) { + System.out.printf("%d->NULL", head.val); + } else { + System.out.printf("%d->", head.val); + } + head = head.next; + } + System.out.println(); + } + + public static void printLinkedList(DoubleLinkedNode head) { + + DoubleLinkedNode originalHead = head; + while (head != null) { + if (head.next == null) { + System.out.printf("%d->NULL", head.val); + } else { + System.out.printf("%d->", head.val); + } + head = head.next; + } + + System.out.println(); + + while (originalHead != null) { + if (originalHead.prev == null) { + System.out.printf("NULL<-%d", originalHead.val); + } else { + System.out.printf("<-%d", originalHead.val); + } + originalHead = originalHead.next; + } + + System.out.println(); + } + +} From 293c81419cbbab89a823af0cf2b8e2e40452e8ec Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 17 Feb 2020 23:50:57 +0800 Subject: [PATCH 57/74] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BF=AE=E6=94=B9,?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=AA=E7=8E=AF=E9=80=80=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/study/linkedlist/FindMiddleOne.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/study/linkedlist/FindMiddleOne.java b/src/main/java/com/study/linkedlist/FindMiddleOne.java index 8c94759..ed19985 100644 --- a/src/main/java/com/study/linkedlist/FindMiddleOne.java +++ b/src/main/java/com/study/linkedlist/FindMiddleOne.java @@ -145,8 +145,10 @@ private static int findMiddle(DoubleLinkedNode head, DoubleLinkedNode tail) { // 链表长度为奇数的情况下,两个指针会走在同一个节点上,则该节点就是中间节点 // 链表长度为偶数的情况下,头指针所在的节点为中间点 - if (newHead == newTail || newHead.next == newTail) + if (newHead == newTail || newHead.next == newTail) { middle = newHead.val; + break; + } newHead = newHead.next;// 头节点往后走 newTail = newTail.prev;// 尾节点往前走 From 5c09a5a1b07621d999cdfc4f9de92251cd13dcfa Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 17 Feb 2020 23:51:45 +0800 Subject: [PATCH 58/74] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/array/MaximumSubarray.java | 15 ++ .../study/binarytree/InorderTraversal.java | 43 +++- .../com/study/binarytree/LevelTraversal.java | 54 +++-- .../com/study/binarytree/MaximumDepth.java | 5 +- .../com/study/binarytree/MinimumDepth.java | 30 ++- .../study/binarytree/PostorderTraversal.java | 40 +++- .../study/binarytree/PreorderTraversal.java | 5 +- .../LowestCommonAncestor.java | 8 +- .../ClimbingStairs3Steps.java | 6 +- .../dynamicprogramming/CountPathMatrix.java | 10 + .../dynamicprogramming/FibonacciSequence.java | 101 +++++++-- .../study/dynamicprogramming/Triangle.java | 56 ++++- .../study/linkedlist/DoubleLinkedNode.java | 210 +----------------- .../com/study/linkedlist/LinkedListCycle.java | 26 +-- .../study/linkedlist/LinkedListCycleTwo.java | 32 +-- .../java/com/study/linkedlist/LinkedNode.java | 10 + .../java/com/study/linkedlist/ListNode.java | 10 - .../com/study/linkedlist/MergeTwoLists.java | 54 ++--- .../java/com/study/linkedlist/Reverse.java | 66 +++--- .../study/linkedlist/SwapNodesInPairs.java | 54 ++--- .../number/BestTimeToBuyAndSellStockII.java | 2 +- .../java/com/study/number/CountPrime.java | 1 + src/main/java/com/study/number/PowXN.java | 57 ++++- src/main/java/com/study/utils/Printer.java | 19 -- 24 files changed, 507 insertions(+), 407 deletions(-) create mode 100644 src/main/java/com/study/linkedlist/LinkedNode.java delete mode 100644 src/main/java/com/study/linkedlist/ListNode.java delete mode 100644 src/main/java/com/study/utils/Printer.java diff --git a/src/main/java/com/study/array/MaximumSubarray.java b/src/main/java/com/study/array/MaximumSubarray.java index 8f07c52..006b104 100644 --- a/src/main/java/com/study/array/MaximumSubarray.java +++ b/src/main/java/com/study/array/MaximumSubarray.java @@ -55,6 +55,21 @@ private static int maxSubArray(int[] nums) { return max; } + private static int maxSubArray_2(int[] nums){ + int max = nums[0]; + int currentSum = nums[0]; + + for(int i =1; i < nums.length; i++){ + if(currentSum > 0) + currentSum += nums[i]; + else + currentSum = nums[i]; + + max = Math.max(max,currentSum); + } + return max; + } + /** * 使用贪心算法 * diff --git a/src/main/java/com/study/binarytree/InorderTraversal.java b/src/main/java/com/study/binarytree/InorderTraversal.java index 06907c2..c5641ae 100644 --- a/src/main/java/com/study/binarytree/InorderTraversal.java +++ b/src/main/java/com/study/binarytree/InorderTraversal.java @@ -31,10 +31,12 @@ public static void main(String[] args) { Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; TreeNode root = TreeUtils.buildTree(arr); + TreeUtils.show(root); //List list = inorderTraversal(root); //List list = inorderTraversal2(root); - List list = inorderTraversal3(root); + //List list = inorderTraversal3(root); + List list = inorderTraversal4(root); for (Integer num : list) { System.out.print(num + " "); } @@ -100,25 +102,58 @@ private static List inorderTraversal3(TreeNode root) { Stack stack = new Stack(); + // 遍历条件为当前节点不为空 或者 栈不为空 while (root != null || !stack.isEmpty()) { - // 对于每个节点都将其所有左节点入栈 + // 将当前节点的所有左节点入栈 // 如果当前节点不为空,递归其左节点 while (root != null) { stack.push(root);//将左节点入栈 - System.out.println(String.format("节点%d被放入栈中",root.val)); + System.out.println(String.format("节点%d被放入栈中", root.val)); root = root.left; } + // 从栈中弹出节点 if (!stack.isEmpty()) { // 将栈中的节点的第一个弹出 // 存在左节点的,优先将最深层左节点弹出 root = stack.pop(); - System.out.println(String.format("节点%d从栈中弹出",root.val)); + System.out.println(String.format("节点%d从栈中弹出", root.val)); list.add(root.val); + System.out.println(String.format("将节点%d放入集合中", root.val)); // 切到右子树,给下一轮找左子树使用 root = root.right; } } return list; } + + private static List inorderTraversal4(TreeNode root) { + List list = new ArrayList<>(); + + if (root == null) + return list; + + Stack stack = new Stack<>(); + + // 中序遍历条件有两个 当前节点不为空 或者 栈不为空 + while (root != null || !stack.isEmpty()) { + + // 将当前节点的所有左边子节点入栈(包括根节点) + while (root != null) { + stack.push(root); + root = root.left; + } + + // 处理栈中的节点 + if (!stack.isEmpty()) { + // 将栈顶节点弹出并放入list + root = stack.pop(); + list.add(root.val); + + // 将节点切到弹出节点的右子节点, 便于下次迭代处理 + root = root.right; + } + } + return list; + } } diff --git a/src/main/java/com/study/binarytree/LevelTraversal.java b/src/main/java/com/study/binarytree/LevelTraversal.java index 3955690..6097d5e 100644 --- a/src/main/java/com/study/binarytree/LevelTraversal.java +++ b/src/main/java/com/study/binarytree/LevelTraversal.java @@ -54,12 +54,9 @@ private static List> levelOrder(TreeNode root) { Queue queue = new LinkedList(); queue.add(root); - // 当前所在的层次 - int level = 0; - while (!queue.isEmpty()) { // 往节点结合中添加当前层级的空节点集合 - levels.add(new ArrayList()); + List currentLevelNodes = new ArrayList<>(); // 获取当前层次的节点个数 int level_length = queue.size(); // 遍历当前层的节点个数 @@ -67,7 +64,7 @@ private static List> levelOrder(TreeNode root) { TreeNode node = queue.remove();//取出并移除队列的第一个节点 根据先进先出原则, 取出的顺序为 根 左 右(根据添加时候的顺序一致) // 往节点集合中添加当前层级的节点 - levels.get(level).add(node.val); + currentLevelNodes.add(node.val); // 往当前层队列中添加当前层的子节点,用于下一次迭代处理 if (node.left != null) @@ -75,8 +72,7 @@ private static List> levelOrder(TreeNode root) { if (node.right != null) queue.add(node.right); } - // 层级+1,进入到下一个层级 - level++; + levels.add(currentLevelNodes); } return levels; } @@ -93,13 +89,12 @@ private static List> levelOrder2(TreeNode root) { if (root == null) return nodes; - int level = 0; Queue queue = new LinkedList(); queue.add(root); // 遍历队列中的节点,当前层里的所有节点 while (queue.size() > 0) { - nodes.add(new ArrayList()); + List currentLevelNodes = new ArrayList<>(); // 当前层节点数 int nodeCount = queue.size(); @@ -108,16 +103,14 @@ private static List> levelOrder2(TreeNode root) { for (int i = 0; i < nodeCount; i++) { // 添加当前层节点值 TreeNode node = queue.remove(); - nodes.get(level).add(node.val); + currentLevelNodes.add(node.val); if (node.left != null) queue.add(node.left); if (node.right != null) queue.add(node.right); } - - // 接着循环下一层 - level++; + nodes.add(currentLevelNodes); } return nodes; } @@ -153,4 +146,39 @@ private static void search(TreeNode root, int level, List> nodeLi search(root.left, level + 1, nodeList); search(root.right, level + 1, nodeList); } + + private static List> levelOrder3(TreeNode root) { + List> nodes = new ArrayList>(); + + if (root == null) + return nodes; + + int level = 0; + Queue queue = new LinkedList(); + queue.add(root); + + // 遍历队列中的节点,当前层里的所有节点 + while (queue.size() > 0) { + nodes.add(new ArrayList()); + + // 当前层节点数 + int nodeCount = queue.size(); + + // 遍历当前层所有的节点, 往节点结合中添加当前层所有节点值, 往队列中添加当前所有节点的子节点 + for (int i = 0; i < nodeCount; i++) { + // 添加当前层节点值 + TreeNode node = queue.remove(); + nodes.get(level).add(node.val); + + if (node.left != null) + queue.add(node.left); + if (node.right != null) + queue.add(node.right); + } + + // 接着循环下一层 + level++; + } + return nodes; + } } diff --git a/src/main/java/com/study/binarytree/MaximumDepth.java b/src/main/java/com/study/binarytree/MaximumDepth.java index 057d893..0dbed94 100644 --- a/src/main/java/com/study/binarytree/MaximumDepth.java +++ b/src/main/java/com/study/binarytree/MaximumDepth.java @@ -40,9 +40,9 @@ public static void main(String[] args) { TreeNode root = TreeUtils.buildTree(arr); TreeUtils.show(root); - //System.out.println(maxDepthByRecursion(root, "", root.val)); + System.out.println(maxDepthByRecursion(root, "根", root.val)); //System.out.println(maxDepthByLoop(root)); - System.out.println(maxDepthByRecursion2(root)); + //System.out.println(maxDepthByRecursion2(root)); } /** @@ -93,6 +93,7 @@ private static int maxDepthByRecursion2(TreeNode root) { int right = maxDepthByRecursion2(root.right); // 从最底层往根节点回溯的过程中, 计算每层的层数, 取左右两边最大的层数并+1(每层的层数) + // 每次回溯到上一层 都会将当前层左右节点的最大层数+1 带给上一层 return Math.max(left, right) + 1;// 最底层为0+1=1, 倒数第2层为0+1+1=2 } diff --git a/src/main/java/com/study/binarytree/MinimumDepth.java b/src/main/java/com/study/binarytree/MinimumDepth.java index f1d7b75..22cf815 100644 --- a/src/main/java/com/study/binarytree/MinimumDepth.java +++ b/src/main/java/com/study/binarytree/MinimumDepth.java @@ -32,17 +32,22 @@ public class MinimumDepth { public static void main(String[] args) { - Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + //Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + //Integer[] arr = {5, 1, 4, null, null, 3}; + Integer[] arr = {5, 1}; TreeNode root = TreeUtils.buildTree(arr); TreeUtils.show(root); //System.out.println(minDepthByRecursion(root)); - System.out.println(minDepthByLoop(root)); + System.out.println(minDepthByRecursion2(root)); + //System.out.println(minDepthByLoop(root)); } /** * 使用分治的方法 从左右节点进行递归, * 注意: 当root节点左右孩子都为空时,返回1 - * + *

+ * 如果是单边为空或者都为空的节点, 返回另一边+1 这样能够记录所有分支的高度, 最后通过min(跟的左分支,根的右分支) 得到深度最小的 + *

* 时间复杂度为O(n) * * @param root @@ -55,6 +60,7 @@ private static int minDepthByRecursion(TreeNode root) { int left = minDepthByRecursion(root.left); int right = minDepthByRecursion(root.right); + // 如果是单边为空或者都为空的节点, 返回另一边+1 这样能够记录所有分支的高度, 最后通过min(跟的左分支,根的右分支) 得到深度最小的 // 如果左边子节点为空的时候, 那么返回右边不为空的孩子深度+1 if (left == 0) return right + 1; @@ -66,9 +72,25 @@ else if (right == 0) } + private static int minDepthByRecursion2(TreeNode root) { + if (root == null) + return 0; + + int left = minDepthByRecursion2(root.left); + int right = minDepthByRecursion2(root.right); + + // 记录所有分支的深度, 最后在根节点上取左右最短的一条 + if (left == 0) + return right + 1; + else if (right == 0) + return left + 1; + else + return Math.min(left, right) + 1; + } + /** * 使用层级遍历的方式: 循环+队列 一层一层遍历并记录层数, 当遇到第一个左右子节点为空的节点, 那这个节点就是最小深度的叶子节点, 返回的层数为最小层数(最小深度). - * + *

* 时间复杂度为O(n), 空间复杂度为O(n) * * @param root diff --git a/src/main/java/com/study/binarytree/PostorderTraversal.java b/src/main/java/com/study/binarytree/PostorderTraversal.java index cda9930..c494102 100644 --- a/src/main/java/com/study/binarytree/PostorderTraversal.java +++ b/src/main/java/com/study/binarytree/PostorderTraversal.java @@ -30,6 +30,8 @@ public static void main(String[] args) { Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; TreeNode root = TreeUtils.buildTree(arr); + TreeUtils.show(root); + //List list = postorderTraversal(root); //List list = postorderTraversal2(root); //List list = postorderTraversal3(root); @@ -37,7 +39,8 @@ public static void main(String[] args) { // for (Integer num : list) { // System.out.print(num + " "); // } - Stack stack = postorderTraversal5(root); + //Stack stack = postorderTraversal5(root); + Stack stack = postorderTraversal5_2(root); while (!stack.isEmpty()) { System.out.print(stack.pop() + " "); } @@ -127,6 +130,14 @@ private static List postorderTraversal3(TreeNode root) { return list; } + /** + * 通过迭代法+栈 实现后序遍历二叉树 + * 遍历出来的每个二叉树节点再使用栈来存储,最后遍历输出 + * 根节点最先进结果栈,最后出结果栈 + * + * @param root + * @return + */ private static Stack postorderTraversal5(TreeNode root) { Stack result = new Stack(); Stack stack = new Stack(); @@ -140,7 +151,7 @@ private static Stack postorderTraversal5(TreeNode root) { while (!stack.isEmpty()) { root = stack.pop(); - // 将节点值加到列表顶部 + // 因为根节点是最后输出的,所以将根节点首先放入结果栈中(先进后出) result.push(root.val); System.out.println(String.format("将节点%d加入到栈中", root.val)); @@ -159,6 +170,31 @@ private static Stack postorderTraversal5(TreeNode root) { return result; } + + private static Stack postorderTraversal5_2(TreeNode root) { + Stack output = new Stack<>(); + Stack input = new Stack<>(); + + if (root == null) { + return output; + } + + input.push(root); + + while (!input.isEmpty()) { + TreeNode node = input.pop(); + output.push(node.val); + + if (node.left != null) + input.push(node.left); + + if (node.right != null) + input.push(node.right); + } + + return output; + } + /** * 迭代遍历 左 右 根 *

diff --git a/src/main/java/com/study/binarytree/PreorderTraversal.java b/src/main/java/com/study/binarytree/PreorderTraversal.java index cb60774..4d61a53 100644 --- a/src/main/java/com/study/binarytree/PreorderTraversal.java +++ b/src/main/java/com/study/binarytree/PreorderTraversal.java @@ -30,11 +30,12 @@ public class PreorderTraversal { public static void main(String[] args) { Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; TreeNode root = TreeUtils.buildTree(arr); + TreeUtils.show(root); //List list = preorderTraversal(root); - //List list = preorderTraversalByLoop(root); + List list = preorderTraversalByLoop(root); // List list = preorderTraversalByLoop2(root); - List list = preorderTraversalByLoopWithList(root); + //List list = preorderTraversalByLoopWithList(root); for (Integer num : list) { System.out.print(num + " "); } diff --git a/src/main/java/com/study/binarytree/binarysearchtree/LowestCommonAncestor.java b/src/main/java/com/study/binarytree/binarysearchtree/LowestCommonAncestor.java index 52412c5..cae67d5 100644 --- a/src/main/java/com/study/binarytree/binarysearchtree/LowestCommonAncestor.java +++ b/src/main/java/com/study/binarytree/binarysearchtree/LowestCommonAncestor.java @@ -87,16 +87,20 @@ private static TreeNode getlca(TreeNode root, TreeNode p, TreeNode q) { /** * 使用二叉搜索树 左子树所有节点 < 父节点 < 右子树所有节点 的原理 * + * 将节点和根节点比较, 从左子树找 或者 右子树找, 不需要全树遍历, 类似二叉查找 + * + * 时间复杂度为O(logN) + * * @param root * @param p * @param q * @return */ private static TreeNode getlca2(TreeNode root, TreeNode p, TreeNode q) { - // 如果 p和q的值 都小于 root, 直接从树的左边去找 + // 如果 p和q的值 都小于 root根节点, 说明两个节点都在左子树, 直接从树的左边去找 if (p.val < root.val && root.val > q.val) return getlca2(root.left, p, q); - // 如果 p和q的值都大于 root, 直接从树的右边去找 + // 如果 p和q的值都大于 root, 说明量两个节点都在右子树, 往树的右边去找 if (p.val > root.val && q.val > root.val) return getlca2(root.right, p, q); // 如果 p.val < root.val < q.val 或 p.val > root.val > q.val 则直接返回root节点 diff --git a/src/main/java/com/study/dynamicprogramming/ClimbingStairs3Steps.java b/src/main/java/com/study/dynamicprogramming/ClimbingStairs3Steps.java index 5dd9a6a..21b45f5 100644 --- a/src/main/java/com/study/dynamicprogramming/ClimbingStairs3Steps.java +++ b/src/main/java/com/study/dynamicprogramming/ClimbingStairs3Steps.java @@ -79,12 +79,15 @@ private static int climbStairsByArray(int n) { if (n == 3) return 4; - // 之后的每层走法数为前面3层的走法之和 + // 定义状态 int[] arr = new int[n]; + + // 初始状态 arr[0] = 1; arr[1] = 2; arr[2] = 4; + // 状态推导 // 从第4个层开始计算 for (int i = 3; i < n; i++) { // 将前3个元素相加得到第4个元素 @@ -92,6 +95,7 @@ private static int climbStairsByArray(int n) { arr[i] = arr[i - 1] + arr[i - 2] + arr[i - 3]; } + // 得出结果 // 返回第n个元素, 因为数组是从第0个元素开始的 return arr[n - 1]; } diff --git a/src/main/java/com/study/dynamicprogramming/CountPathMatrix.java b/src/main/java/com/study/dynamicprogramming/CountPathMatrix.java index 85fef83..c2d6e88 100644 --- a/src/main/java/com/study/dynamicprogramming/CountPathMatrix.java +++ b/src/main/java/com/study/dynamicprogramming/CountPathMatrix.java @@ -39,9 +39,11 @@ public static void main(String[] args) { * @return */ private static int uniquePaths(int m, int n) { + // 状态定义 // 创建一个宽为m,长为n int[][] matrix = new int[m][n]; + // 初始状态 // 先将横坐标为0 和 纵坐标为0的所有表格都填上, 走法都是一种. // 横坐标为0的只能从左往右横着走. 纵坐标为0的只能从上往下竖着走 for (int i = 0; i < m; i++) { @@ -51,12 +53,15 @@ private static int uniquePaths(int m, int n) { matrix[0][j] = 1; } + // 状态推导 // 从横纵坐标不为0的格子开始推倒 for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { matrix[i][j] = matrix[i - 1][j] + matrix[i][j - 1]; } } + + // 得出结果 //返回走后一个格子的走法数量, 由于数组是从0开始,所以实际最大索引为长度-1 return matrix[m - 1][n - 1]; } @@ -93,13 +98,16 @@ private static int uniquePaths2(int m, int n) { } private static int uniquePaths2_2(int m, int n) { + // 定义状态 int[] pre = new int[n]; int[] cur = new int[n]; + // 初始状态 //将第1列和第2列用1进行填充 Arrays.fill(pre, 1); Arrays.fill(cur, 1); + // 状态推导 for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { // 某一个格子的走法等于下方和右方走法之和 @@ -107,6 +115,8 @@ private static int uniquePaths2_2(int m, int n) { } pre = cur; } + + // 得出结果 // 返回最后一列最后一行的元素 走法数 return pre[n - 1]; } diff --git a/src/main/java/com/study/dynamicprogramming/FibonacciSequence.java b/src/main/java/com/study/dynamicprogramming/FibonacciSequence.java index b3c3ed5..7f63385 100644 --- a/src/main/java/com/study/dynamicprogramming/FibonacciSequence.java +++ b/src/main/java/com/study/dynamicprogramming/FibonacciSequence.java @@ -14,28 +14,76 @@ public class FibonacciSequence { public static void main(String[] args) { - int n = 1; - int[] cache = new int[n]; - //System.out.println(String.format("第%d个数为: %d", n, fibByRecursion(n)); - System.out.println(String.format("第%d个数为: %d", n, fibByRecursion2(n, cache))); - //System.out.println(String.format("第%d个数为: %d", n, fibByLoop(n))); - //System.out.println(String.format("第%d个数为: %d", n, fibByDP(n))); +// int n = 1; +// int[] cache = new int[n]; +// //System.out.println(String.format("第%d个数为: %d", n, fibByRecursion(n)); +// System.out.println(String.format("第%d个数为: %d", n, fibByRecursion2(n, cache))); +// //System.out.println(String.format("第%d个数为: %d", n, fibByLoop(n))); +// //System.out.println(String.format("第%d个数为: %d", n, fibByDP(n))); +// +// n = 5; +// cache = new int[n]; +// //System.out.println(String.format("第%d个数为: %d", n, fibByRecursion(n)); +// System.out.println(String.format("第%d个数为: %d", n, fibByRecursion2(n, cache))); +// //System.out.println(String.format("第%d个数为: %d", n, fibByLoop(n))); +// //System.out.println(String.format("第%d个数为: %d", n, fibByDP(n))); +// +// n = 6; +// cache = new int[n]; +// //System.out.println(String.format("第%d个数为: %d", n, fibByRecursion(n)); +// System.out.println(String.format("第%d个数为: %d", n, fibByRecursion2(n, cache))); +// //System.out.println(String.format("第%d个数为: %d", n, fibByLoop(n))); +// //System.out.println(String.format("第%d个数为: %d", n, fibByDP(n))); + + + // 计算双递归的时间复杂度问题 时间复杂度为O(2^n) + int n = 2; + fibByRecursion_count(n); + System.out.println(String.format("n: %d, 计算次数: %d",n, count)); + count = 0; n = 5; - cache = new int[n]; - //System.out.println(String.format("第%d个数为: %d", n, fibByRecursion(n)); - System.out.println(String.format("第%d个数为: %d", n, fibByRecursion2(n, cache))); - //System.out.println(String.format("第%d个数为: %d", n, fibByLoop(n))); - //System.out.println(String.format("第%d个数为: %d", n, fibByDP(n))); - - n = 6; - cache = new int[n]; - //System.out.println(String.format("第%d个数为: %d", n, fibByRecursion(n)); - System.out.println(String.format("第%d个数为: %d", n, fibByRecursion2(n, cache))); - //System.out.println(String.format("第%d个数为: %d", n, fibByLoop(n))); - //System.out.println(String.format("第%d个数为: %d", n, fibByDP(n))); + fibByRecursion_count(n); + System.out.println(String.format("n: %d, 计算次数: %d",n, count)); + count = 0; + + n = 10; + fibByRecursion_count(n); + System.out.println(String.format("n: %d, 计算次数: %d",n, count)); + count = 0; + + n = 15; + fibByRecursion_count(n); + System.out.println(String.format("n: %d, 计算次数: %d",n, count)); + count = 0; + + n = 20; + fibByRecursion_count(n); + System.out.println(String.format("n: %d, 计算次数: %d",n, count)); + count = 0; + + n = 30; + fibByRecursion_count(n); + System.out.println(String.format("n: %d, 计算次数: %d",n, count)); + count = 0; + + n = 40; + fibByRecursion_count(n); + System.out.println(String.format("n: %d, 计算次数: %d",n, count)); + count = 0; + + // 由于递归的执行次数是指数级上涨, 当n为50的时候, 机器就算不出来了 +// n = 50; +// fibByRecursion_count(n); +// System.out.println(String.format("n: %d, 计算次数: %d",n, count)); +// count = 0; } + /** + * 计算递归的执行次数 + */ + private static int count = 0; + /** * 求第n个数的值, 使用分治的方法 使用两个递归分别求出n-1和n-2两数的值,然后进行相加得到第n个数的值 *

@@ -50,6 +98,8 @@ private static int fibByRecursion(int n) { if (n == 0) return 0; + count++; + // 斐波拉契的第1和第2个数为1 // 临界条件 n = 1 和 n=2 if (n == 1 || n == 2) { @@ -62,6 +112,21 @@ private static int fibByRecursion(int n) { return result; } + private static int fibByRecursion_count(int n) { + count++; + + if (n == 0) + return 0; + + // 斐波拉契的第1和第2个数为1 + // 临界条件 n = 1 和 n=2 + if (n == 1 || n == 2) { + return 1; + } + return fibByRecursion_count(n - 1) + fibByRecursion_count(n - 2); //第n个数为前两个数之后 比如 5 = 3 +2; + } + + /** * 由于递归过程中计算了大量元素, 我们可以使用一个数组cache来对其进行优化 * diff --git a/src/main/java/com/study/dynamicprogramming/Triangle.java b/src/main/java/com/study/dynamicprogramming/Triangle.java index a09036f..d01ea0b 100644 --- a/src/main/java/com/study/dynamicprogramming/Triangle.java +++ b/src/main/java/com/study/dynamicprogramming/Triangle.java @@ -39,15 +39,17 @@ public static void main(String[] args) { triangle.add(Arrays.asList(6, 5, 7)); triangle.add(Arrays.asList(4, 1, 8, 3)); - System.out.println(minimumTotal(triangle)); + //System.out.println(minimumTotal(triangle)); //System.out.println(minimumTotal2(triangle)); + + System.out.println("最小路径和为: " + minimumTotal_dfs(triangle)); } /** * 动态规划法 *

* 从后往前推, 修改每个点的值为其最小路径和, 一层层往上,最终顶点的最短路径 = 顶点值 + 第二层最短路径的那个点的值 - * + *

* 2 * 3 4 * 6 5 7 @@ -88,6 +90,56 @@ public static int minimumTotal(List> triangle) { return triangle.get(0).get(0); } + private static int minSum = Integer.MAX_VALUE; + + public static int minimumTotal_dfs(List> triangle) { + if (triangle.size() == 0 || triangle.get(0).size() == 0) { + return 0; + } + + dfs(triangle, 0, 0, 0, ""); + return minSum; + } + + /** + * 使用深度优先进行暴力求解, 得出所有不同的路径和, 然后取其中最小的 + * 类似二叉树的先序遍历 + * + * 时间复杂度为O(2^N) + * + * @param triangle + * @param i + * @param j + * @param sum + * @return + */ + private static void dfs(List> triangle, int i, int j, int sum, String path) { + // terminator 终止条件 + if (i == triangle.size() - 1) { + // 递归到达最后一行结束, 返回路径和 + sum += triangle.get(i).get(j); + System.out.println(sum); // 输出所有路径之和 + minSum = Math.min(sum, minSum); + + path += triangle.get(i).get(j); + System.out.println(path); // 输出所有路径 + + return; + } + + // 将每层路径之和相加 + sum += triangle.get(i).get(j); + path += triangle.get(i).get(j) + "->"; + + // 每个元素往下有两种方式可走, 一个是i+1,j 一个是i+1,j+1 + // 往每层正下方遍历 + dfs(triangle, i + 1, j, sum, path); + //System.out.println(); + // 往每层正下方+1遍历 + dfs(triangle, i + 1, j + 1, sum, path); + //System.out.println(); + } + public static int minimumTotal2(List> triangle) { diff --git a/src/main/java/com/study/linkedlist/DoubleLinkedNode.java b/src/main/java/com/study/linkedlist/DoubleLinkedNode.java index 0ee9b27..6f78d54 100644 --- a/src/main/java/com/study/linkedlist/DoubleLinkedNode.java +++ b/src/main/java/com/study/linkedlist/DoubleLinkedNode.java @@ -1,211 +1,11 @@ package com.study.linkedlist; -/** - * 双向升序链表 - *

- * 每个节点都有直接前驱后直接后继, 除了第一个节点没有直接前驱和最后一个节点没有直接后继 - * 结构如下 - *

- * 1 -> 2 -> 4 -> 5 -> 6 -> null - * null <- 1 <- 2 <- 4 <- 5 <- 6 - */ public class DoubleLinkedNode { + public int val; + public DoubleLinkedNode prev; + public DoubleLinkedNode next; - public static void main(String[] args) { - //往 1 -> 2 -> 4 -> 5 -> 6 中插入 一个0或3 - // <- <- <- <- - Node node1 = new Node(1); - Node node2 = new Node(2); - Node node3 = new Node(3); - Node node4 = new Node(4); - Node node5 = new Node(5); - Node node6 = new Node(6); - - node1.next = node2; - node2.prev = node1; - node2.next = node3; - node3.prev = node2; - node3.next = node4; - node4.prev = node3; - node4.next = node5; - node5.prev = node4; - node5.next = node6; - node6.prev = node5; - -// printLinkedList(node1); -// -// Node head1 = insertNode(node1, 0); -// -// printLinkedList(head1); -// System.out.println(); -// -// Node head2 = insertNode(head1, 8); -// -// printLinkedList(head2); -// System.out.println(); -// Node head3 = insertNode(head2, 4); -// -// printLinkedList(head3); - - System.out.println("---------------------"); - Node head4 = deleteNode(node1, 3); - printLinkedList(head4); - -// System.out.println(); -// head4 = deleteNode(head4, 0); -// printLinkedList(head4); -// -// System.out.println(); -// head4 = deleteNode(head4, 5); -// printLinkedList(head4); -// -// System.out.println(); -// head4 = deleteNode(head4, 8); -// printLinkedList(head4); - } - - - /** - * 插入一个新value并返回插入后的头节点 - *

- * 新插入的节点可能是第一个,也可能是中间或者最后一个 - * - * @param head - * @param value - * @return - */ - private static Node insertNode(Node head, int value) { - Node newNode = new Node(value); - Node originalHead = head; - while (head != null) { - // 如果插入的节点比链表里的每个节点都小 - if (newNode.value < head.value) { - // 头节点 - // 如果head是头节点, 将新节点插在头节点的前面 - if (head.prev == null) { - newNode.next = head; - head.prev = newNode; - return newNode; - } else { - // 中间节点 - //如果当前head不是头节点,插入新节点的时候需要修改上一个节点的next,当前节点的pre和next, 以及下一个节点的pre, 4个指针 - //修改当前节点的上一个节点, 将上一个节点存一个临时变量 - Node prevNode = head.prev; - //将上一个节点的临时变量的后继指针指向新节点, 将新节点的前驱指针指向上一个节点的临时变量 - prevNode.next = newNode; - newNode.prev = prevNode; - // 将当前节点的前驱指向新节点, 新节点的后继指向当前节点 - head.prev = newNode; - newNode.next = head; - return originalHead; - } - } - // 末尾节点 - //如果新插入的节点比链表最后一个还要大 - if (head.next == null) { - //修改当前节点的后继和新节点的前驱 - head.next = newNode; - newNode.prev = head; - return originalHead; - } - head = head.next; - } - return originalHead; - } - - - /** - * 删除一个值对应的节点,并返回新的头节点 - *

- * 该值对应的节点可能不存在, 也可能是头,中,尾部位的节点 - * - * @param head - * @param value - */ - private static Node deleteNode(Node head, int value) { - System.out.println("要删除的节点是" + value); - Node deleteNode = new Node(value); - Node originalHead = head; - while (head != null) { - // 如果要删除的节点存在 - if (head.value == deleteNode.value) { - // 如果删除的节点是头节点, 将头节点的next和下一个节点prev改为null - if (head.prev == null) { - Node secondNode = head.next; - secondNode.prev = null; - head.next = null; //这行代码也可以不写 因为我们返回的链表secondNode里面已经没有了head,所以head.next也可以不处理 - System.out.println("删除的节点为头节点"); - return secondNode; //返回新的头结点,第二个节点 - } else { - //删除的节点是中间节点和尾节点 - //如果删除的节点是尾节点 - if (head.next == null) { - // 将尾节点的prev和上一个节点的next改为null - Node prev = head.prev; - prev.next = null; - head.prev = null; //这行代码也可以不写 - System.out.println("删除的节点为尾节点"); - - } else { //如果删除的节点为中间的一个节点 - //将要删除的节点的上一个节点next改为要删除节点的下一个节点, 将要删除节点的下一个节点的prev改为它的上一个节点 - - //将当前节点的上一个和下一个节点拿出来存临时变量 - Node prev = head.prev; - Node next = head.next; - //将当前节点的前驱和后继改为null - //head.prev = head.next = null; //这行代码也可以不写 - //将上一个节点的next指向下一个节点 - prev.next = next; - //将下一个节点的prev指向上一个节点 - next.prev = prev; - - System.out.println("删除的节点为中间节点"); - } - return originalHead; //返回原来的头结点 - } - } - head = head.next; - } - System.out.println("要删除的节点不存在"); - // 如果删除的节点不存在 - return originalHead; - } - - - private static void printLinkedList(Node head) { - - Node originalHead = head; - while (head != null) { - if (head.next == null) { - System.out.printf("%d->NULL", head.value); - } else { - System.out.printf("%d->", head.value); - } - head = head.next; - } - - System.out.println(); - - while (originalHead != null) { - if (originalHead.prev == null) { - System.out.printf("NULL<-%d", originalHead.value); - } else { - System.out.printf("<-%d", originalHead.value); - } - originalHead = originalHead.next; - } - - System.out.println(); + public DoubleLinkedNode(int val) { + this.val = val; } } - - -class Node { - public int value; - public Node prev; - public Node next; - - public Node(int value) { - this.value = value; - } -} \ No newline at end of file diff --git a/src/main/java/com/study/linkedlist/LinkedListCycle.java b/src/main/java/com/study/linkedlist/LinkedListCycle.java index c493209..bb1b007 100644 --- a/src/main/java/com/study/linkedlist/LinkedListCycle.java +++ b/src/main/java/com/study/linkedlist/LinkedListCycle.java @@ -1,7 +1,5 @@ package com.study.linkedlist; -import com.study.utils.Printer; - import java.util.HashSet; import java.util.Set; @@ -22,11 +20,11 @@ */ public class LinkedListCycle { public static void main(String[] args) { - 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); + LinkedNode node1 = new LinkedNode(1); + LinkedNode node2 = new LinkedNode(2); + LinkedNode node3 = new LinkedNode(3); + LinkedNode node4 = new LinkedNode(4); + LinkedNode node5 = new LinkedNode(5); node1.next = node2; node2.next = node3; @@ -48,10 +46,10 @@ public static void main(String[] args) { * @param head * @return */ - private static boolean hasCycle(ListNode head) { - ListNode fast = head; - ListNode slow = head; - while (fast != null && slow != null && fast.next != null) { + private static boolean hasCycle(LinkedNode head) { + LinkedNode fast = head; + LinkedNode slow = head; + while (fast != null && slow != null && fast.next != null) {// 因为快指针需要走两步,如果fast.next为null,那走第二步就报空指针了 // 注意因为起始位置都一样,都是head,所以需要让两个指针都先跑起来,之后再相遇 slow = slow.next; fast = fast.next.next; @@ -73,8 +71,8 @@ private static boolean hasCycle(ListNode head) { * @param head * @return */ - private static boolean hasCycle2(ListNode head) { - Set set = new HashSet(); + private static boolean hasCycle2(LinkedNode head) { + Set set = new HashSet(); while (head != null) { if (set.contains(head)) { @@ -92,7 +90,7 @@ private static boolean hasCycle2(ListNode head) { * @param head * @return */ - private static boolean hasCycle3(ListNode head) { + private static boolean hasCycle3(LinkedNode head) { long start = System.currentTimeMillis(); while (head != null) { head = head.next; diff --git a/src/main/java/com/study/linkedlist/LinkedListCycleTwo.java b/src/main/java/com/study/linkedlist/LinkedListCycleTwo.java index 22b7d09..3cb2289 100644 --- a/src/main/java/com/study/linkedlist/LinkedListCycleTwo.java +++ b/src/main/java/com/study/linkedlist/LinkedListCycleTwo.java @@ -41,11 +41,11 @@ */ public class LinkedListCycleTwo { public static void main(String[] args) { - 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); + LinkedNode node1 = new LinkedNode(1); + LinkedNode node2 = new LinkedNode(2); + LinkedNode node3 = new LinkedNode(3); + LinkedNode node4 = new LinkedNode(4); + LinkedNode node5 = new LinkedNode(5); node1.next = node2; node2.next = node3; @@ -53,7 +53,7 @@ public static void main(String[] args) { node4.next = node5; // 节点5之后又回到了节点3 node5.next = node3; - ListNode cycyleBegins = detectCycle(node1); + LinkedNode cycyleBegins = detectCycle(node1); //ListNode cycyleBegins = detectCycle2(node1); //ListNode cycyleBegins = detectCycle3(node1); System.out.println(cycyleBegins.val); @@ -83,12 +83,12 @@ public static void main(String[] args) { * @param head * @return */ - private static ListNode detectCycle(ListNode head) { + private static LinkedNode detectCycle(LinkedNode head) { if (head == null || head.next == null) return null; - ListNode slow = head; - ListNode fast = head; - ListNode start = head; + LinkedNode slow = head; + LinkedNode fast = head; + LinkedNode start = head; boolean isCycle = false; // 使用快慢节点,找出是否为有还链表 @@ -122,11 +122,11 @@ private static ListNode detectCycle(ListNode head) { * @param head * @return */ - private static ListNode detectCycle2(ListNode head) { - ListNode fast = head; - ListNode slow = head; + private static LinkedNode detectCycle2(LinkedNode head) { + LinkedNode fast = head; + LinkedNode slow = head; - ListNode start = head; + LinkedNode start = head; while (fast != null && slow != null && fast.next != null) { fast = fast.next.next; slow = slow.next; @@ -150,8 +150,8 @@ private static ListNode detectCycle2(ListNode head) { *

* 存储前判断是否已存在该节点,存在则返回该节点. */ - private static ListNode detectCycle3(ListNode head) { - Set set = new HashSet(); + private static LinkedNode detectCycle3(LinkedNode head) { + Set set = new HashSet(); while (head != null) { if (set.contains(head)) { diff --git a/src/main/java/com/study/linkedlist/LinkedNode.java b/src/main/java/com/study/linkedlist/LinkedNode.java new file mode 100644 index 0000000..0d6860f --- /dev/null +++ b/src/main/java/com/study/linkedlist/LinkedNode.java @@ -0,0 +1,10 @@ +package com.study.linkedlist; + +public class LinkedNode { + public int val; + public LinkedNode next; + + public LinkedNode(int x) { + val = x; + } +} diff --git a/src/main/java/com/study/linkedlist/ListNode.java b/src/main/java/com/study/linkedlist/ListNode.java deleted file mode 100644 index 15de10a..0000000 --- a/src/main/java/com/study/linkedlist/ListNode.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.study.linkedlist; - -public class ListNode { - public int val; - public ListNode next; - - public ListNode(int x) { - val = x; - } -} diff --git a/src/main/java/com/study/linkedlist/MergeTwoLists.java b/src/main/java/com/study/linkedlist/MergeTwoLists.java index 58ed6ef..4e34126 100644 --- a/src/main/java/com/study/linkedlist/MergeTwoLists.java +++ b/src/main/java/com/study/linkedlist/MergeTwoLists.java @@ -1,6 +1,6 @@ package com.study.linkedlist; -import com.study.utils.Printer; +import com.study.utils.LinkedListUtils; /** * 合并两个有序链表 @@ -18,24 +18,24 @@ public class MergeTwoLists { public static void main(String[] args) { - ListNode l1 = new ListNode(1); - l1.next = new ListNode(2); - l1.next.next = new ListNode(4); + LinkedNode l1 = new LinkedNode(1); + l1.next = new LinkedNode(2); + l1.next.next = new LinkedNode(4); - ListNode l2 = new ListNode(1); - l2.next = new ListNode(3); - l2.next.next = new ListNode(4); - l2.next.next.next = new ListNode(6); + LinkedNode l2 = new LinkedNode(1); + l2.next = new LinkedNode(3); + l2.next.next = new LinkedNode(4); + l2.next.next.next = new LinkedNode(6); System.out.println("l1链表"); - Printer.printLinkedList(l1); + LinkedListUtils.printLinkedList(l1); System.out.println("l2链表"); - Printer.printLinkedList(l2); + LinkedListUtils.printLinkedList(l2); // ListNode result = mergeTwoLists(l1, l2); // ListNode result = mergeTwoListsByRecursion(l1,l2); - ListNode result = mergeTwoListsByRecursion2(l1, l2); - Printer.printLinkedList(result); + LinkedNode result = mergeTwoListsByRecursion2(l1, l2); + LinkedListUtils.printLinkedList(result); } /** @@ -48,21 +48,21 @@ public static void main(String[] args) { * @param l2 * @return */ - public static ListNode mergeTwoLists(ListNode l1, ListNode l2) { - ListNode prev = new ListNode(-1); - ListNode head = prev; + public static LinkedNode mergeTwoLists(LinkedNode l1, LinkedNode l2) { + LinkedNode prev = new LinkedNode(-1); + LinkedNode head = prev; // 先将l1和l2中小的元素依次放入新链表中 while (l1 != null && l2 != null) { // 将小的加入到链表前面 if (l1.val > l2.val) { - head.next = new ListNode(l2.val); + head.next = new LinkedNode(l2.val); l2 = l2.next; } else { - head.next = new ListNode(l1.val); + head.next = new LinkedNode(l1.val); l1 = l1.next; } - Printer.printLinkedList(prev.next); + LinkedListUtils.printLinkedList(prev.next); head = head.next; } @@ -85,7 +85,7 @@ public static ListNode mergeTwoLists(ListNode l1, ListNode l2) { // 针对上面注释的代码进行优化 // 因为最终只可能有一条链表没有遍历完成, 而且链表本身也是有序的,所以直接加到新链表的next即可 head.next = l1 == null ? l2 : l1; - Printer.printLinkedList(prev.next); + LinkedListUtils.printLinkedList(prev.next); return prev.next; } @@ -100,44 +100,44 @@ public static ListNode mergeTwoLists(ListNode l1, ListNode l2) { * @param l2 * @return */ - public static ListNode mergeTwoListsByRecursion(ListNode l1, ListNode l2) { + public static LinkedNode mergeTwoListsByRecursion(LinkedNode l1, LinkedNode l2) { // 如果l1递归结束,说明l2剩下的节点比l1大, 于是返回l2, 让前面的l1.next接上 if (l1 == null) { System.out.print("l1递归往内终止完成, l2 = "); - Printer.printLinkedList(l2); + LinkedListUtils.printLinkedList(l2); return l2; } // 如果l2递归结束, 说明l1剩下的节点比l2大, 于是返回l1, 让前面的l2.next接上 else if (l2 == null) { System.out.print("l2递归往内终止完成, l1 = "); - Printer.printLinkedList(l1); + LinkedListUtils.printLinkedList(l1); return l1; // 递归的顺序是从小的节点开始往内递归, 从小往大,所以到了最内层结束都的节点都是最大的. 之后从内层外外计算,是从大往小计算. } else if (l1.val < l2.val) { // 如果l1的当前节点比l2小, 那么l1.next与排序好的表头相接, 为什么是接表头? 因为递归是从里往外开始计算的, // 里是最后一个节点(最大的节点), 而l1是前面的小节点, 顺序是从小到大 System.out.println("l1递归往内找终止条件"); - Printer.printLinkedList(l1); + LinkedListUtils.printLinkedList(l1); // 对l1进行递归遍历 // 真正计算是从递归最里层往外 l1.next = mergeTwoListsByRecursion(l1.next, l2);// l1.next 后面接的是递归里层的比自己大的节点 System.out.println("l1递归往外,开始计算:"); - Printer.printLinkedList(l1); + LinkedListUtils.printLinkedList(l1); return l1;// 返回l1, 供外层递归的 l1或者l2中的 } else { // 如果l2的节点小于l1, 那么使用l2的next与排序好的表头相接 System.out.println("l2递归往内找终止条件"); - Printer.printLinkedList(l2); + LinkedListUtils.printLinkedList(l2); // 对l2开始递归, 真正计算是从递归最里层往外, 从小往大, l2.next 接的节点都是比它大的 l2.next = mergeTwoListsByRecursion(l1, l2.next); System.out.println("l2递归往外,开始计算:"); - Printer.printLinkedList(l2); + LinkedListUtils.printLinkedList(l2); return l2; } } - public static ListNode mergeTwoListsByRecursion2(ListNode l1, ListNode l2) { + public static LinkedNode mergeTwoListsByRecursion2(LinkedNode l1, LinkedNode l2) { // 哪个链表先递归到终止条件为null, 说明另一个链表的剩下节点比该链表大, 于是返回对方剩余链表, 加在自己的.next后面 if (l1 == null) { return l2; diff --git a/src/main/java/com/study/linkedlist/Reverse.java b/src/main/java/com/study/linkedlist/Reverse.java index d877c26..0ab5908 100644 --- a/src/main/java/com/study/linkedlist/Reverse.java +++ b/src/main/java/com/study/linkedlist/Reverse.java @@ -1,6 +1,6 @@ package com.study.linkedlist; -import com.study.utils.Printer; +import com.study.utils.LinkedListUtils; import java.util.Stack; @@ -19,22 +19,22 @@ public class Reverse { public static void main(String[] args) { - 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); + LinkedNode node1 = new LinkedNode(1); + LinkedNode node2 = new LinkedNode(2); + LinkedNode node3 = new LinkedNode(3); + LinkedNode node4 = new LinkedNode(4); + LinkedNode node5 = new LinkedNode(5); node1.next = node2; node2.next = node3; node3.next = node4; node4.next = node5; - Printer.printLinkedList(node1); + LinkedListUtils.printLinkedList(node1); System.out.println(); //Printer.printLinkedList(reverseList(node1)); //Printer.printLinkedList(reverseListByRecursion(node1)); - Printer.printLinkedList(reverseListByRecursion2(node1)); + LinkedListUtils.printLinkedList(reverseListByRecursion2(node1)); //Printer.printLinkedList(reverseList3(node1)); //Printer.printLinkedList(reverseList4(node1)); //Printer.printLinkedList(reverstListByStack(node1)); @@ -48,14 +48,14 @@ public static void main(String[] args) { * @param head * @return */ - private static ListNode reverseList(ListNode head) { + private static LinkedNode reverseList(LinkedNode head) { // cur的上一个节点 - ListNode prev = null; + LinkedNode prev = null; // 将当前节点指针指向head节点 - ListNode cur = head; + LinkedNode cur = head; while (null != cur) { // 将当前节点指针的下一个节点暂存起来 - ListNode next = cur.next; + LinkedNode next = cur.next; // 反转指针,将指针从后一个节点转为指向前一个节点 // 比如2->3 变成 2->1 cur.next = prev; @@ -68,13 +68,13 @@ private static ListNode reverseList(ListNode head) { } - private static ListNode reverseList4(ListNode head) { - ListNode prev = null; + private static LinkedNode reverseList4(LinkedNode head) { + LinkedNode prev = null; - ListNode cur = head; + LinkedNode cur = head; while (cur != null) { - ListNode next = cur.next; + LinkedNode next = cur.next; cur.next = prev; @@ -105,19 +105,19 @@ private static ListNode reverseList4(ListNode head) { * @param head * @return */ - private static ListNode reverseListByRecursion(ListNode head) { + private static LinkedNode reverseListByRecursion(LinkedNode head) { if (head == null || head.next == null) { System.out.print("递归往内终止, head = "); - Printer.printLinkedList(head); + LinkedListUtils.printLinkedList(head); return head; } System.out.print("递归往内"); - Printer.printLinkedList(head); + LinkedListUtils.printLinkedList(head); // 首先进入递归,由于head不为空,且head.next不为空,所以直到最后一个head为5的时候,才满足条件,结束递归,返回prev为5->null,head.next为5->null,head为4->5->null - ListNode prev = reverseListByRecursion(head.next); + LinkedNode prev = reverseListByRecursion(head.next); System.out.print("递归往外"); - Printer.printLinkedList(head); + LinkedListUtils.printLinkedList(head); // 使得head.next.head.next成为一个闭环 // 赋值前4->5->null // 赋值head后 4->5->4->5->4->.....成为一个闭环 @@ -129,11 +129,11 @@ private static ListNode reverseListByRecursion(ListNode head) { return prev; } - private static ListNode reverseListByRecursion2(ListNode head) { + private static LinkedNode reverseListByRecursion2(LinkedNode head) { if (head == null || head.next == null) { return head; } - ListNode prev = reverseListByRecursion2(head.next); + LinkedNode prev = reverseListByRecursion2(head.next); head.next.next = head; head.next = null; return prev; @@ -146,8 +146,8 @@ private static ListNode reverseListByRecursion2(ListNode head) { * @param head * @return */ - private static ListNode reverseList3(ListNode head) { - Stack stack = new Stack(); + private static LinkedNode reverseList3(LinkedNode head) { + Stack stack = new Stack(); while (head != null) { stack.push(head); @@ -155,15 +155,15 @@ private static ListNode reverseList3(ListNode head) { } // 创建一个新节点prev,也是一个只有单个节点的链表 - ListNode prev = new ListNode(-1); + LinkedNode prev = new LinkedNode(-1); // 将head指向prev head = prev; while (!stack.isEmpty()) { - ListNode current = stack.pop(); + LinkedNode current = stack.pop(); // 因为stack中取出的节点可能带有原始链表的后继节点,所以重新创建一个相同val的新节点,移除原有的next属性 // 给prev链表的添加下一个节点 - head.next = new ListNode(current.val); + head.next = new LinkedNode(current.val); //将指针指向head下一个节点 head = head.next; } @@ -177,17 +177,17 @@ private static ListNode reverseList3(ListNode head) { * @param head * @return */ - private static ListNode reverstListByStack(ListNode head) { + private static LinkedNode reverstListByStack(LinkedNode head) { // 先将链表每个节点压入栈中, 放入顺序为 1 2 3 4 5 - Stack stack = new Stack(); + Stack stack = new Stack(); while (head != null) { - stack.push(new ListNode(head.val));// 将节点压栈的时候需要放入没有后继的节点 + stack.push(new LinkedNode(head.val));// 将节点压栈的时候需要放入没有后继的节点 head = head.next; } // 把栈顶部的元素一个个弹出, 弹出顺序为5 4 3 2 1 - ListNode newNode = new ListNode(0); - ListNode newHead = newNode; + LinkedNode newNode = new LinkedNode(0); + LinkedNode newHead = newNode; while (stack.size() > 0) { newHead.next = stack.pop(); newHead = newHead.next; diff --git a/src/main/java/com/study/linkedlist/SwapNodesInPairs.java b/src/main/java/com/study/linkedlist/SwapNodesInPairs.java index 3933b56..9d43d09 100644 --- a/src/main/java/com/study/linkedlist/SwapNodesInPairs.java +++ b/src/main/java/com/study/linkedlist/SwapNodesInPairs.java @@ -1,6 +1,6 @@ package com.study.linkedlist; -import com.study.utils.Printer; +import com.study.utils.LinkedListUtils; /** * 两两交换链表中的节点 @@ -20,12 +20,12 @@ public class SwapNodesInPairs { public static void main(String[] args) { - 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); - ListNode node6 = new ListNode(6); + LinkedNode node1 = new LinkedNode(1); + LinkedNode node2 = new LinkedNode(2); + LinkedNode node3 = new LinkedNode(3); + LinkedNode node4 = new LinkedNode(4); + LinkedNode node5 = new LinkedNode(5); + LinkedNode node6 = new LinkedNode(6); node1.next = node2; node2.next = node3; @@ -33,11 +33,11 @@ public static void main(String[] args) { node4.next = node5; node5.next = node6; - Printer.printLinkedList(node1); + LinkedListUtils.printLinkedList(node1); //Printer.printLinkedList(swapPairs(node1)); //Printer.printLinkedList(swapPairs2(node1)); - Printer.printLinkedList(swapPairs3(node1)); + LinkedListUtils.printLinkedList(swapPairs3(node1)); } /** @@ -48,19 +48,21 @@ public static void main(String[] args) { * @param head * @return */ - private static ListNode swapPairs(ListNode head) { - ListNode result = new ListNode(-1); + private static LinkedNode swapPairs(LinkedNode head) { + LinkedNode result = new LinkedNode(-1); result.next = head; - ListNode prev = result; + LinkedNode prev = result; while (prev.next != null && prev.next.next != null) { - ListNode a = prev.next; //1 - ListNode b = prev.next.next; //2 + LinkedNode a = prev.next; //1 + LinkedNode b = prev.next.next; //2 - prev.next = b; //2 - a.next = b.next; //3 - b.next = a; //1 + prev.next = b; //2 prev -> 2 + a.next = b.next; //3 1 -> 3 + b.next = a; //1 2 -> 1 + + // 经过上面的交换后 变成了 prev -> 2 ->1 - >3 // prev.next = a.next = 3, prev.next.next = 4 // prev = a; // 也可以 (a = prev.next.next) prev = prev.next.next; @@ -75,18 +77,18 @@ private static ListNode swapPairs(ListNode head) { * @param head * @return */ - private static ListNode swapPairs3(ListNode head){ - ListNode headPrev = new ListNode(0); + private static LinkedNode swapPairs3(LinkedNode head){ + LinkedNode headPrev = new LinkedNode(0); headPrev.next = head; //在头节点前面在放一个前置节点, 用于后面的迭代使用 - ListNode prev = headPrev; + LinkedNode prev = headPrev; // 每次循环都判断接下来的 一对节点不为空, 然后对他们进行对调 while(prev.next !=null && prev.next.next !=null){ - ListNode oldFirst = prev.next; - ListNode oldSecond = prev.next.next; + LinkedNode oldFirst = prev.next; + LinkedNode oldSecond = prev.next.next; //将当前循环的第一和第二节点进行对换 - ListNode third = oldSecond.next; + LinkedNode third = oldSecond.next; prev.next = oldSecond; oldSecond.next = oldFirst; oldFirst.next = third; @@ -104,17 +106,17 @@ private static ListNode swapPairs3(ListNode head){ * @param head * @return */ - private static ListNode swapPairs2(ListNode head) { + private static LinkedNode swapPairs2(LinkedNode head) { if (head == null || head.next == null){ return head; } // prev 为最后一个节点 // prev = null, head = 5, head = 3 - ListNode prev = swapPairs2(head.next.next); + LinkedNode prev = swapPairs2(head.next.next); // tmp 为倒数第二个节点 // 下面3行代码将每一对的两个节点进行互换 // 将tmp指向当前pair对的第2个节点 - ListNode tmp = head.next; //tmp2 = 6,tmp2 = 4->5->null, tmp2 = 2->3->6->5->null + LinkedNode tmp = head.next; //tmp2 = 6,tmp2 = 4->5->null, tmp2 = 2->3->6->5->null // 将当前pair对第2个节点指向已经反转好的节点 head.next = prev; // 5.next = null, 3.next = 6->5->null, 1.next = 4->3->6->5->null // 将pair对的第3个节点的指向第1个节点, 这样第2个节点后面为第1个节点,形成了反转 diff --git a/src/main/java/com/study/number/BestTimeToBuyAndSellStockII.java b/src/main/java/com/study/number/BestTimeToBuyAndSellStockII.java index 4c7249f..54e0f01 100644 --- a/src/main/java/com/study/number/BestTimeToBuyAndSellStockII.java +++ b/src/main/java/com/study/number/BestTimeToBuyAndSellStockII.java @@ -68,7 +68,7 @@ public static void main(String[] args) { public static int maxProfit(int[] prices) { int profit = 0; for (int i = 0; i < prices.length; i++) { - // 前一个数小于后一个就买, 因为先买再卖能赚 + // 只要下一个股票比当前股票贵,我就买当前的,然后卖下一个,获取下一个和当前之间的利润 if (prices.length > i + 1 && prices[i] < prices[i + 1]) { profit += prices[i + 1] - prices[i]; } diff --git a/src/main/java/com/study/number/CountPrime.java b/src/main/java/com/study/number/CountPrime.java index 31b5471..2d9f0ab 100644 --- a/src/main/java/com/study/number/CountPrime.java +++ b/src/main/java/com/study/number/CountPrime.java @@ -52,6 +52,7 @@ public boolean isPrime(int num) { // return false; // } // } + // 如果一个数如果只能被 1 和它本身整除, 那就是质数 for (int i = 2; i < num; i++) { if (num % i == 0) { return false; diff --git a/src/main/java/com/study/number/PowXN.java b/src/main/java/com/study/number/PowXN.java index 5cf8e79..d5eea48 100644 --- a/src/main/java/com/study/number/PowXN.java +++ b/src/main/java/com/study/number/PowXN.java @@ -28,13 +28,13 @@ public class PowXN { /** * 解法: 一种是暴力解法, 利用循环或者递归的方式 将x乘以自身n次. 时间复杂度O(n) - * 另一种是用递归+分治的解法, 将递归的次数减少到logN次. + * 另一种是用递归+分治的解法, 将递归的次数减少到logN次. * 注意: 需要考虑n为负数和奇偶数的情况. * * @param args */ public static void main(String[] args) { - double result = myPow(2.1000, 10); + double result = myPow(2.0, 10); System.out.println(result); // // double result = myPow2(2.1000, 3); @@ -48,7 +48,10 @@ public static void main(String[] args) { // System.out.println(result); //result = myPowRecursion(2, 10); - result = myPowRecursion(2.1000, 10); + result = myPowRecursion(2.0, 10); + System.out.println(result); + + result = fastPow3(2.0, 10); System.out.println(result); // result = myPowRecursion2(2, 10); @@ -167,7 +170,8 @@ public static double fastPow(double x, long n) { double beforeHalf = half; // 每一次执行会将上一递归的half结果拿来相乘,这样每次的half都是上一次的half的平方或者平方乘以x // 需要考虑n为偶数和奇数的情况 - if (n % 2 == 0) { + //if (n % 2 == 0) { + if ((n & 1) == 0) { half = half * half; System.out.println(String.format("往外递归 n = %d, half = %s * %s = %s", n, beforeHalf, beforeHalf, half)); } else { @@ -203,9 +207,16 @@ public static double fastPow2(double x, long n) { return 1.0; } - double half = fastPow2(x, n / 2); + long N = n; + if (n < 0) { + x = 1 / x; + N = -N; + } - if (n % 2 == 0) { + double half = fastPow2(x, N / 2); + + //if (N % 2 == 0) { + if ((N & 1) == 0) { half = half * half; } else { half = half * half * x; @@ -214,6 +225,40 @@ public static double fastPow2(double x, long n) { } + /** + * 使用迭代的方法 每次进行n/2次求解, 时间复杂度为O(logN) + * + * @param x + * @param n + * @return + */ + public static double fastPow3(double x, long n) { + if (n == 0) { + return 1.0; + } + + // 处理n为负数的情况 + long N = n; + if (n < 0) { + x = 1 / x; // x为倒数 + N = -N; + } + + double result = 1; + + // x^10 = x^2 * x^2 * x^2 * x^2 * x^2; + while (N > 0) { + if ((N & 1) == 1) { + result *= x; + } + result *= x * x; + N >>= 1; // 相当于N = N / 2; + } + + return result; + } + + /** * 暴利法(递归) 利用递归的方式将 x乘以自己n次, 效果和迭代一样. 时间复杂度O(n) * diff --git a/src/main/java/com/study/utils/Printer.java b/src/main/java/com/study/utils/Printer.java deleted file mode 100644 index a2d6699..0000000 --- a/src/main/java/com/study/utils/Printer.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.study.utils; - -import com.study.linkedlist.ListNode; - -public final class Printer { - - public static void printLinkedList(ListNode head) { - while (head != null) { - if (head.next == null) { - System.out.printf("%d->NULL", head.val); - } else { - System.out.printf("%d->", head.val); - } - head = head.next; - } - System.out.println(); - } - -} From c7584a5bde6ffb341b70420630a644e858f7446e Mon Sep 17 00:00:00 2001 From: longwu Date: Tue, 18 Feb 2020 14:41:20 +0800 Subject: [PATCH 59/74] =?UTF-8?q?=E4=BA=8C=E5=88=86=E6=9F=A5=E6=89=BE4?= =?UTF-8?q?=E7=A7=8D=E5=8F=98=E5=BD=A2=E9=A2=98:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 找到第一个相同的元素(存在重复的元素) 找到最后一个相同的元素(存在重复的元素) 找到第一个大于等于的元素(存在重复的元素) 找到最后一个小于等于的元素(存在重复的元素) --- .../java/com/study/search/BinarySearch.java | 14 +- .../java/com/study/search/BinarySearch2.java | 389 ++++++++++++++++++ src/main/java/com/study/search/FindLastK.java | 128 ++++++ 3 files changed, 525 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/study/search/BinarySearch2.java create mode 100644 src/main/java/com/study/search/FindLastK.java diff --git a/src/main/java/com/study/search/BinarySearch.java b/src/main/java/com/study/search/BinarySearch.java index dfaa357..c224e8b 100644 --- a/src/main/java/com/study/search/BinarySearch.java +++ b/src/main/java/com/study/search/BinarySearch.java @@ -26,14 +26,15 @@ public static void main(String[] args) { * @return */ private static int search(int[] arr, int key) { - int low, high, mid; //第1个元素索引 - low = 0; + int low = 0; + //最后一个元素索引 - high = arr.length - 1; + int high = arr.length - 1; while (low <= high) { // 先从中间开始查找 - mid = (low + high) / 2; + //int mid = (low + high) / 2; // low + high 可能会大于 Integer.MAX_VALUE + int mid = (high - low) >> 1 + low; // 如果小于中间的,就把范围改成最左边到中间-1 if (key < arr[mid]) { high = mid - 1; @@ -51,7 +52,7 @@ private static int search(int[] arr, int key) { /** - * 折半查找,每次查找都基于中间值来对比 来缩小查找范围, 时间复杂度为O1 + * 折半查找,每次查找都基于中间值来对比 来缩小查找范围, 时间复杂度为O(logN) * * @param arr * @param element @@ -62,7 +63,8 @@ private static int search2(int[] arr, int element) { // 循环条件是最低位索引小于等于最高位索引 while (low <= high) { - int mid = (low + high) / 2; + //mid = (low + high) / 2; // low + high 可能会大于 Integer.MAX_VALUE + int mid = (high - low) >> 1 + low; // 如果元素大于中间值, 则下次从右边查找 if (arr[mid] < element) { // 将最小索引改为中间+! diff --git a/src/main/java/com/study/search/BinarySearch2.java b/src/main/java/com/study/search/BinarySearch2.java new file mode 100644 index 0000000..bb0fb03 --- /dev/null +++ b/src/main/java/com/study/search/BinarySearch2.java @@ -0,0 +1,389 @@ +package com.study.search; + +/** + * 二分查找4种变形题: + * + * 找到第一个相同的元素(存在重复的元素) + * 找到最后一个相同的元素(存在重复的元素) + * 找到第一个大于等于的元素(存在重复的元素) + * 找到最后一个小于等于的元素(存在重复的元素) + * + */ +public class BinarySearch2 { + + /** + * ((right - left) >> 1) + left 等价于 (left + right) / 2 + * + * @param args + */ + public static void main(String[] args) { + //int[] arr = {-1, 0, 1, 2, 4, 5}; + //int[] arr = {0}; + int[] arr = {}; + System.out.println(find(arr, 0)); + System.out.println(find(arr, -1)); + System.out.println(find(arr, 1)); + System.out.println(find(arr, 2)); + System.out.println(find(arr, 4)); + System.out.println(find(arr, 5)); + System.out.println("---------------查找第一个相同元素"); + arr = new int[]{-2, -1, 0, 2, 2, 3, 3, 5}; + //arr = new int[]{}; + System.out.println(findFirst(arr, -2)); + System.out.println(findFirst(arr, 0)); + System.out.println(findFirst(arr, 2)); + System.out.println(findFirst(arr, 3)); + System.out.println(findFirst(arr, 5)); + System.out.println("---------------查找最后一个相同元素"); + System.out.println(findLast(arr, -2)); + System.out.println(findLast(arr, 0)); + System.out.println(findLast(arr, 2)); + System.out.println(findLast(arr, 3)); + System.out.println(findLast(arr, 5)); + System.out.println("---------------查找第一个大于等于元素"); + arr = new int[]{-2, 0, 2, 2, 3, 3, 5}; + System.out.println(findFirstLargerEquals4(arr, -2)); //0 + System.out.println(findFirstLargerEquals4(arr, -1)); //1 + System.out.println(findFirstLargerEquals4(arr, 1)); //2 + System.out.println(findFirstLargerEquals4(arr, 5)); //6 + System.out.println(findFirstLargerEquals4(arr, 2)); //2 + System.out.println(findFirstLargerEquals4(arr, 7)); //-1 没找到 + System.out.println(findFirstLargerEquals4(arr, 0)); //1 + + arr = new int[]{-2}; + System.out.println(findFirstLargerEquals4(arr, -3)); //0 + + arr = new int[]{}; + System.out.println(findFirstLargerEquals4(arr, -3)); //-1 + + System.out.println("---------------查找最后一个小于等于元素"); + arr = new int[]{-2, 0, 2, 2, 3, 3, 5}; + System.out.println(findLastSmallerEquals2(arr, -2)); //0 + System.out.println(findLastSmallerEquals2(arr, -1)); //0 + System.out.println(findLastSmallerEquals2(arr, 1)); //1 + System.out.println(findLastSmallerEquals2(arr, 5)); //6 + System.out.println(findLastSmallerEquals2(arr, 2)); //3 + System.out.println(findLastSmallerEquals2(arr, 7)); //6 + System.out.println(findLastSmallerEquals2(arr, 0)); //1 + +// System.out.println(findLastLargerEquals(arr, 3)); +// System.out.println(findLastLargerEquals(arr, 4)); + } + + + /** + * 在一个有序数组中(没有重复的元素),找到目标元素所在的位置 + * + * @param arr + * @param target + * @return + */ + private static int find(int[] arr, int target) { + int left = 0, right = arr.length - 1; + + while (left <= right) { + //int mid = (right - left) >> 1 + left;// 由于+left的优先级高, 会先于前面的 >> 1执行, 这样查找某些元素的时候会出现死循环 + int mid = ((right - left) >> 1) + left; // 左边需要加括号 + //int mid2 = (left + right) / 2; + //System.out.println(String.format("mid: %d, mid2: %d", mid, mid2)); + if (arr[mid] == target) { + return mid;// 找到目标元素直接返回 + // 当前元素大于目标元素 + } else if (arr[mid] > target) { + right = mid - 1; // 往前找 + } else { + left = mid + 1; // 否则往后找 + } + } + // 找不到或者空数组返回-1 + return -1; + } + + /** + * 找到第一个相同的元素(存在重复的元素) + * + * 如果找到, 再往前找, 找到的条件是前一个小于目标元素, 那么当前这个就是目标元素. (往前找需要考虑边界值为0,已经找到第一个了还等于目标,那直接返回) + * + * @param array + * @param target + * @return + */ + public static int findFirst(int[] array, int target) { + int left = 0, right = array.length - 1; + + while (left <= right) { + int mid = ((right - left) >> 1) + left; + if (array[mid] > target) { + right = mid - 1; //往前查找 + } else if (array[mid] < target) { + left = mid + 1; // 往后查找 + } else { + // 如果找到相同的元素, 并且前面一个不是该元素, 说明找到了结果 + // 两种情况, 该元素已经是数组第一个元素了(前面没有了元素) 或者 前面的元素不是目标元素 + if (mid == 0 || array[mid - 1] != target) { + return mid; + } else { + // 否则 继续往左查找 + right = mid - 1; + } + } + } + + return -1; + } + + + //3.变式:查找最后一个值等于给定值的元素( 存在重复的数据) + public int binarySearch5(int[] array, int target) { + int left = 0, mid = 0, right = array.length - 1; + + while (left <= right) { + mid = (right - left) >> 1 + left; + + if (array[mid] < target) { + left = mid + 1; + } else if (array[mid] > target) { + right = mid - 1; + } else { //相等的前提下 + if (mid == array.length - 1 || (array[mid + 1] != target)) { + return mid; + } else { + left = mid + 1; //往后找 + } + } + } + + return -1; + } + + /** + * 找到最后一个相同的元素, + * + * 找到的基础上往后找, 如果后一个大于目标元素,则当前为最后一个相同元素. 边界值为当前元素为最后一个 + * + * @return + */ + private static int findLast(int[] arr, int target) { + int left = 0, right = arr.length - 1; + + while (left <= right) { + int mid = ((right - left) >> 1) + left; + if (arr[mid] > target) { + right = mid - 1; //往前找 + } else if (arr[mid] < target) { + left = mid + 1; //往后找 + } else { + // 如果下一个元素不是目标元素, 说明当前这个为目标最后一个元素 + // 两种情况, 该元素已经是数组最后一个(不用再找) 或者 下一个不是目标元素(该元素不是最后一个) + if (mid == arr.length - 1 || arr[mid + 1] != target) + return mid; + else + left = mid + 1; //继续往右找 + } + } + return -1; + } + +// /** +// * 查找第一个大于等于给定值的元素( 存在重复的数据) +// * +// * @param array +// * @param target +// * @return +// */ +// public static int binarySearch4(int[] array, int target) { +// int left = 0, right = array.length - 1; +// +// while (left <= right) { +// int mid = ((right - left) >> 1) + left; +// +// if (array[mid] >= target) { +// if (mid == array.length - 1 || (array[mid + 1] != target)) { +// return mid; +// } else { +// right = mid - 1; +// } +// } else { +// left = mid + 1; //往后找 +// } +// } +// +// return -1; +// } + + /** + * 查找第一个大于等于给定值的元素(存在重复的数据) + * + * @param arr + * @param target + * @return + */ + public static int findFirstLargerEquals(int[] arr, int target) { + int left = 0; + int right = arr.length - 1; + + while (left <= right) { + int mid = ((right - left) >> 1) + left; + // 小于目标, 往后找 + if (arr[mid] < target) { + left = mid + 1; + } else { + // 大于或者等于目标, 需要判断是否已经找到, 如果没找到再往前找 + // 如果已经处于第0个元素 或者 前一个元素小于目标, 说明已经找到 + if (mid == 0 || arr[mid - 1] < target) { + return mid; + } else { + // 否则 继续往前找, 将越来越接近第一个大于等于的元素 + right = mid - 1; + } + } + } + + // 找不到 返回-1 + return -1; + } + + /** + * 找到 第一个大于等于目标元素的位置 + *

+ * 思路, 先找大于等于的元素, 找到以后再里面判断是否是第一个大于等于的(因为大于等于的可能很多), 如果不是,再往前找第一个大于等于的 + *

+ * 第一个大于等于的标志为 上一个元素小于目标 (或者 当前位置为第0个, 后面的元素都大于等于目标 边界值) + * + * @param arr + * @param target + * @return + */ + public static int findFirstLargerEquals3(int[] arr, int target) { + int left = 0; + int right = arr.length - 1; + + while (left <= right) { + int mid = ((right - left) >> 1) + left; + // 如果小于,完全不满足条件,往后找 + if (arr[mid] < target) { + left = mid + 1; + } else { + // 大于或者等于的情况, 因为大于等于的目标元素非常多,所以需要判断是否为第一个 + // 如果当前找到的位置是第0个元素, 说明后面的元素都比目标元素大, 那第0个就是结果 (或者数组就一个元素) + // 或者前一个元素小于目标, 那当前元素肯定是第一个大于等于的元素 + if (mid == 0 || arr[mid - 1] < target) { + return mid; + } else { + // 如果没找到目标, 就往前找, 最终一定能找到第一个大于等于的 + right = mid - 1; + } + } + } + return -1; + } + + public static int findFirstLargerEquals4(int[] arr, int target) { + int left = 0; + int right = arr.length - 1; + + while (left <= right) { + int mid = ((right - left) >> 1) + left; + // 大于等于 往前找 + if (arr[mid] >= target) { + // 大于或者等于的情况, 因为大于等于的目标元素非常多,所以需要判断是否为第一个 + // 需要考虑一些边界值, 如果已经找到了第一个元素 还比目标大, 那就直接返回这个元素 + // 或者前一个元素小于目标, 那当前元素肯定是第一个大于等于的元素 + if (mid == 0 || arr[mid - 1] < target) { + return mid; + } else { + // 如果没找到目标, 就往前找, 最终一定能找到第一个大于等于的 + right = mid - 1; + } + } else { + // 如果小于,完全不满足条件,往后找 + left = mid + 1; + } + } + return -1; + } + + /** + * 查找最后一个小于等于给定值的元素( 存在重复的数据) + *

+ * 思路: 如果大于就往前找, 如果小于等于 就往后找(因为小于等于目标元素的特别多), 直到找到最后一个小于等于目标元素的 + *

+ * https://blog.csdn.net/hyforthy/article/details/22614247 + * + * @param array + * @param target + * @return + */ + private static int findLastSmallerEquals(int[] array, int target) { + int left = 0, right = array.length - 1; + + while (left <= right) { + int mid = ((right - left) >> 1) + left; + // 如果大于就往前找 + if (array[mid] > target) { + right = mid - 1; + } else { + // 如果小于等于 就往后找, 直到找到目标元素 + // 下一个元素大于目标, 那当前元素为最后一个小于等于目标的 + // 需要考虑一些边界值, 如果已经找到了最后一个元素 还比目标小, 那就直接返回这个元素 + if (mid == array.length - 1 || array[mid + 1] > target) + return mid; + else // 否则继续往后找 + left = mid + 1; + } + } + return -1; + } + + /** + * 找最后一个小于等于目标元素的索引(目标元素之前的最后一个) + *

+ * 思路: 先排除大于的, 如果大于目标元素就往前找. 如果小于等于目标元素, + * 就往后找, 找到的条件是 下一个大于目标元素. (这里需要考虑边界问题,就是最后一个元素还是小于目标元素(整个数组都比目标元素小),那就直接返回最后一个) + * + * @param array + * @param target + * @return + */ + private static int findLastSmallerEquals2(int[] array, int target) { + int left = 0; + int right = array.length - 1; + + while (left <= right) { + int mid = ((right - left) >> 1) + left; + // 当前元素大于目标元素 往前找 + if (array[mid] > target) { + right = mid - 1; + } else { + // 当前元素小于等于目标元素, 往后找 + if (mid == array.length - 1 || array[mid + 1] > target) + return mid; + else + left = mid + 1; + } + } + // 没找到 + return -1; + } + + //5.变式:查找最后一个小于等于给定值的元素( 存在重复的数据) +// public static int binarySearch6(int[] array, int target) { +// int left = 0, mid = 0, right = array.length - 1; +// +// while (left <= right) { +// mid = (right - left) >> 1 + left; +// +// if (array[mid] <= target) { +// if (mid == array.length - 1 || (array[mid + 1] != target)) { +// return mid; +// } else { +// left = mid + 1; +// } +// } else {//当前值比 参考值 大, 往前找 +// right = mid - 1; +// } +// } +// +// return -1; +// } +} diff --git a/src/main/java/com/study/search/FindLastK.java b/src/main/java/com/study/search/FindLastK.java new file mode 100644 index 0000000..8b8e556 --- /dev/null +++ b/src/main/java/com/study/search/FindLastK.java @@ -0,0 +1,128 @@ +package com.study.search; + +/** + * 找到数组中最后一个目标元素 + */ +public class FindLastK { + + public static void main(String[] args) { + //int[] arr = {1, 2, 2, 2, 2, 2, 3, 3, 4, 5}; + int[] arr = {1, 2, 3, 3, 4, 5}; + //int[] arr = {2}; + System.out.println(find(arr, 2)); + //System.out.println(find2(arr, 2)); + } + + /** +// private static int findLastSameElement(int[] arr, int target) { +// +// if (arr.length == 1) { +// if (arr[0] == target) { +// return 0; +// } else { +// return -1; +// } +// } +// +// int left = 0; +// int right = arr.length - 1; +// +// +// while (left <= right) { +// int middle = (left + right) / 2; +// // 当前位置元素大于目标元素 +// if (arr[middle] > target) { +// // 如果上一个元素为目标元素 +// if (arr[middle - 1] > 0 && arr[middle - 1] == target) { +// return middle - 1; // 返回上一个元素索引 +// } else { +// // 从左边找 +// right = middle - 1; +// } +// // 当前位置元素小于目标元素 +// } else if (arr[middle] < target) { +// // 从右边找 +// left = middle + 1; +// } else { +// // 目标元素 +// // 后面存在目标元素, 继续往后找 +// if (arr[middle + 1] < arr.length && arr[middle + 1] == target) { +// left = middle; +// // 如果后面元素大于目标元素, 那当前元素就是结果 +// } else if (arr[middle + 1] < arr.length && arr[middle + 1] > target) { +// return middle; +// } +// } +// } +// return -1; +// } + */ + + private static int find(int[] arr, int target) { + + int left = 0; + int right = arr.length - 1; + int mid = 0; + while (left <= right) { + mid = (left + right) >> 1; + // 目标元素小于当前元素 + if (arr[mid] > target) { + right = mid - 1; //把范围放到左边找 + // 目标元素大于当前元素 + } else if (arr[mid] < target) { + left = mid + 1; //把范围放到右边找 + } else { + // 找到值相等的元素, 需要判断下一个是否为非值相同的元素 + // 如果下一个值与目标元素不同,则说明当前元素为目标元素 + // 这里需要特殊考虑一下数组只有一个元素的情况 + if (mid == arr.length - 1 || (mid + 1 <= arr.length -1 && arr[mid + 1] != target)) { // 索引+1或者-1需要考虑数组越界的情况 + return mid; + } else {//否则继续往右找,直到找到下一个元素值与目标元素不同 + left = mid + 1; + } + } + } + return -1; + } + + public static int find2(int[] array, int target) { + int left = 0, mid = 0, right = array.length - 1; + + while (left <= right) { + mid = (right - left) >> 1 + left; + + if (array[mid] < target) { + left = mid + 1; + } else if (array[mid] > target) { + right = mid - 1; + } else { //相等的前提下 + if (mid == array.length - 1 || (array[mid + 1] != target)) { + return mid; + } else { + left = mid + 1; //往后找 + } + } + } + return -1; + } + + + private static int findLastK2(int[] arr, int target) { + int left = 0; + int right = arr.length - 1; + + while (left < right) { + int temp = (left + right + 1) >> 1; //保证取到中间靠后的位置 + //int temp = (left + right + 1) / 2; //保证取到中间靠后的位置 + // 当前位置元素大于目标元素 + if (arr[temp] > target) { + // 往左边找, 位置-1 + right = temp - 1; + } else { + // 如果当前位置元素小于或等于目标元素, 往右边找 + left = temp; + } + } + return right; + } +} From a72cd32aa4aeb6fa524804c3f45b2dc483b5d99c Mon Sep 17 00:00:00 2001 From: longwu Date: Tue, 18 Feb 2020 14:44:54 +0800 Subject: [PATCH 60/74] =?UTF-8?q?=E6=89=BE=E5=87=BA=E6=9C=89=E5=BA=8F?= =?UTF-8?q?=E6=95=B0=E7=BB=84=E4=B8=AD=E7=BB=9D=E5=AF=B9=E5=80=BC=E6=9C=80?= =?UTF-8?q?=E5=B0=8F=E7=9A=84=E5=85=83=E7=B4=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/study/search/MinAbs.java | 27 +++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/study/search/MinAbs.java b/src/main/java/com/study/search/MinAbs.java index 8c8382d..356449d 100644 --- a/src/main/java/com/study/search/MinAbs.java +++ b/src/main/java/com/study/search/MinAbs.java @@ -10,15 +10,18 @@ public static void main(String[] args) { //int[] arr = {-3,-2,-1,5}; //int[] arr = {-3, -2, -1, 0, 3, 5}; - int[] arr = {-1, 1, 3, 5}; + //int[] arr = {-1, 1, 3, 5}; //int[] arr = {1, 3, 5}; //int[] arr = {-9, -6, - 2, 0}; + //int[] arr = {-6}; + int[] arr = {}; System.out.println(min(arr)); } /** * 利用二分查找进行,使得时间复杂度为O(logN), 低于遍历的O(N) + * * @param arr * @return */ @@ -26,40 +29,36 @@ private static int min(int[] arr) { int left = 0; int right = arr.length - 1; - if (arr[left] >= 0) - return arr[left]; - if (arr[right] <= 0) - return arr[right]; - while (left <= right) { - int middle = (left + right) / 2; + //int middle = (left + right) / 2; // 这段代码可能越界 + int middle = left + (right - left) >>1; //防止越界 // 大于0往左找最近的负数 if (arr[middle] > 0) { // 判断下一个数是否为负数 - if (arr[middle - 1] < 0) { + if (middle - 1 >= 0 && arr[middle - 1] < 0) { // 返回正负交届的绝对值最小的那个数 if (Math.abs(arr[middle]) > Math.abs(arr[middle - 1])) return arr[middle - 1]; else return arr[middle]; } else - right = middle; + right = middle - 1; //从左边找, 右边界需要减一,因为有边界这个元素不满足 // 小于0往右找最近的正数 } else if (arr[middle] < 0) { - // 判断下一个数是否为正数 - if (arr[middle + 1] > 0) + // 判断下一个数是否为正数 (注意边界) + if (middle + 1 < arr.length && arr[middle + 1] > 0) // 返回正负交届的绝对值最小的那个数 if (Math.abs(arr[middle]) > Math.abs(arr[middle + 1])) return arr[middle + 1]; else return arr[middle]; else - left = middle; + left = middle + 1; // 从左边找 左边界需要+1,因为边界这个元素不满足 } else { - // 如果为0,直接返回 (0是所有正数中绝对值最小的) + // 如果为0,直接返回 (0是所有数中绝对值最小的) return arr[middle]; } } - return 0; + return -1; } } From 2f4d4baab1f4a927d1dc80148d10400c7049fda0 Mon Sep 17 00:00:00 2001 From: longwu Date: Sun, 23 Feb 2020 21:20:45 +0800 Subject: [PATCH 61/74] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=9C=89=E5=BA=8F?= =?UTF-8?q?=E6=88=AA=E6=96=AD=E6=95=B0=E7=BB=84=E7=9A=84=E6=9F=A5=E6=89=BE?= =?UTF-8?q?=E9=97=AE=E9=A2=98,=E6=89=BE=E6=9C=80=E5=B0=8F=E5=80=BC?= =?UTF-8?q?=E5=92=8C=E7=9B=AE=E6=A0=87=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/study/search/FindSmall.java | 282 ++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 src/main/java/com/study/search/FindSmall.java diff --git a/src/main/java/com/study/search/FindSmall.java b/src/main/java/com/study/search/FindSmall.java new file mode 100644 index 0000000..223f99c --- /dev/null +++ b/src/main/java/com/study/search/FindSmall.java @@ -0,0 +1,282 @@ +package com.study.search; + +/** + * 旋转数组的二分查找问题 + *

+ * 截取一个有序数组的后面一段放到前面, 求整个数组中的最小元素 + *

+ * 比如有序数组为 [2, 3, 6, 7, 8, 10, 12, 15], 将后面的10,12,15截取后放到数组的最前面,变成[10, 12, 15, 2, 3, 6, 7, 8] + *

+ * 问题一: 请写一个算法, 找出最小值 + *

+ * 问题二: 给定一个数,查找其在序列中是否存在(返回其位置),请问如何实现? + *

+ * 问题三: 给定任意一个序列,找出其中的一个谷/峰,谷的定义为两边的数均大于某个数。 + *

+ * https://segmentfault.com/q/1010000000181714 + *

+ * https://blog.csdn.net/weixin_34018169/article/details/89698260?utm_source=distribute.pc_relevant.none-task + */ +public class FindSmall { + + public static void main(String[] args) { + int[] arr = {10, 12, 15, 2, 3, 6, 7, 8}; + System.out.println("------找最小值"); + System.out.println(findMin(arr)); // 2 + System.out.println(findMin2(arr)); // 2 + System.out.println("------找目标值"); + System.out.println(findTarget(arr, 2)); //3 + System.out.println(findTarget(arr, 10));//0 + System.out.println(findTarget(arr, 12));//1 + System.out.println(findTarget(arr, 15));//2 + System.out.println(findTarget(arr, 8));//7 + System.out.println(findTarget2(arr, 2)); //3 + System.out.println(findTarget2(arr, 10));//0 + System.out.println(findTarget2(arr, 12));//1 + System.out.println(findTarget2(arr, 15));//2 + System.out.println(findTarget2(arr, 8));//7 + + arr = new int[]{10, 12, 15, 2}; + System.out.println("------找最小值"); + System.out.println(findMin(arr)); // 2 + System.out.println(findMin2(arr)); // 2 + System.out.println("------找目标值"); + System.out.println(findTarget(arr, 2));//3 + System.out.println(findTarget(arr, 10));//0 + System.out.println(findTarget(arr, 12));//1 + System.out.println(findTarget(arr, 15));//2 + arr = new int[]{10, 2}; + System.out.println("------找最小值"); + System.out.println(findMin(arr)); // 2 + System.out.println(findMin2(arr)); // 2 + System.out.println("------找目标值"); + System.out.println(findTarget(arr, 2));//1 + System.out.println(findTarget(arr, 10));//0 + System.out.println(findTarget(arr, 12));//-1 + arr = new int[]{2}; + System.out.println("------找最小值"); + System.out.println(findMin(arr)); // 2 只有一个元素的时候,返回本身 + System.out.println(findMin2(arr)); // 2 + System.out.println("------找目标值"); + System.out.println(findTarget(arr, 2));//0 + System.out.println(findTarget(arr, 10));//-1 + + arr = new int[]{3, 4, 5, 1, 1, 2}; + //arr = new int[]{1, 0, 1, 1, 1}; + System.out.println("------找最小值"); + System.out.println(findMin(arr)); // 2 只有一个元素的时候,返回本身 + System.out.println(findMin2(arr)); // 2 + System.out.println("------找目标值"); + System.out.println(findTarget(arr, 2));//5 + System.out.println(findTarget(arr, 3));//0 + System.out.println(findTarget(arr, 1));//4 + } + + /** + * 问题一: 请写一个算法, 找出最小值 + *

+ * 思路: 跟第0个元素作比较, 如果大于或等于第0个元素, 就往后找. 如果小于第0个元素, 就往前找. + *

+ * 找到的条件是 当前元素小于前一个元素, 那么当前元素就是最小元素. + *

+ * 注意: 需要考虑数组只有一个元素的情况 + *

+ * 时间复杂度O(logN) + * + * @param arr + * @return + */ + private static int findMin(int[] arr) { + int left = 0, right = arr.length - 1; + + if (arr.length == 1) + return arr[0]; + + // 找到目标为前面一个大于后面 + while (left <= right) { + // 如果数组长度为1或者2的时候, mid长度为0 + int mid = ((right - left) >> 1) + left; + // 当前元素大于等于第一个, 往后找 + if (arr[mid] >= arr[0]) { + left = mid + 1; + } else + // 当前元素小于第一个,往前找 + // 找到条件为上一个大于当前这个 + if (mid == 0 || arr[mid] < arr[mid - 1]) //通常mid == 0 || mid < mid -1 是常用的手法. 这样能避免mid-1越界, 也能处理数组长度为1的情况 + return arr[mid]; + else// 否则继续往前走 + right = mid - 1; + } + return -1; + } + + /** + * 问题一: 请写一个算法, 找出最小值 + * 解法二 + * https://blog.csdn.net/weixin_34018169/article/details/89698260?utm_source=distribute.pc_relevant.none-task + * + * @param nums + * @return + */ + public static int findMin2(int nums[]) { + if (nums == null || nums.length == 0) { + throw new RuntimeException("input error!"); + } + // 如果只有一个元素,直接返回 + if (nums.length == 1) + return nums[0]; + int result = nums[0]; + int low = 0, high = nums.length - 1; + int mid; + // 确保 low 下标对应的值在左边的递增子数组,high 对应的值在右边递增子数组 + while (nums[low] >= nums[high]) { + // 确保循环结束条件 + if (high - low == 1) { + return nums[high]; + } + // 取中间位置 + mid = (low + high) / 2; + // 代表中间元素在左边递增子数组 + if (nums[mid] >= nums[low]) { + low = mid; + } else { + high = mid; + } + } + return result; + } + + /** + * 给定一个数,查找其在序列中是否存在(返回其位置),请问如何实现? + * + * 实现方案一: 按 目标元素所处的位置(前半段或后半段), 当前元素和目标元素大小比较, 当前元素的位置(前半段或后半段) 的顺序来处理 + *

+ * 思路: 需要考虑目标元素和当前元素所处的位置, 与第0个元素做比较. + * 如果目标元素在前半段, + * 当前元素小于目标元素 + * 当前元素在前半段 怎么做 + * 当前元素在后半段 怎么做 + * + * 当前元素大于目标元素 + * 当前元素[肯定]在前半段 怎么做 + * + * 目标元素在后半段 + 当前元素小于目标元素 + * 当前元素[肯定]在后半段 怎么做 + * + * 当前元素大于目标元素 + * 当前元素在前半段 怎么做 + * 当前元素在后半段 怎么做 + * + * @param arr + * @param target + * @return + */ + private static int findTarget(int[] arr, int target) { + int left = 0, right = arr.length - 1; + + while (left <= right) { + int mid = ((right - left) >> 1) + left; + // 目标元素在前半段 + if (target >= arr[0]) { + // 如果当前元素小于目标元素 + if (arr[mid] < target) { + // 需要判断一下当前元素所处的区域是前半段还是后半段 + // 如果当前元素在前半段 + if (arr[mid] >= arr[0]) { + //往后找 + left = mid + 1; + } else { + //如果当前元素在后半段 + //往前找 + right = mid - 1; + } + //如果当前元素大于目标元素 + //当前元素也在前半段 + } else if (arr[mid] > target) { + //直接往前找即可 + right = mid - 1; + } else { + // 在前半段找到目标 + return mid; + } + //如果目标元素在后半段 + } else { + // 当前元素小于目标元素 + if (arr[mid] < target) { + //往后找即可 + left = mid + 1; + // 当前元素大于目标元素 + } else if (arr[mid] > target) { + // 需要判断一下当前元素处于前后哪个半段 + // 如果在前半段 + if (arr[mid] >= arr[0]) { + // 往后找 + left = mid + 1; + } else { + //在后半段,往前找即可 + right = mid - 1; + } + } else { + // 在后半段找到目标 + return mid; + } + } + } + return -1; + } + + /** + * 实现方案二: 按 目标元素所处的位置(前半段或后半段), 当前元素的位置(前半段或后半段), 当前元素和目标元素大小比较 的顺序来处理 + * + * @param arr + * @param target + * @return + */ + private static int findTarget2(int[] arr, int target) { + int left = 0, right = arr.length - 1; + + while (left <= right) { + int mid = ((right - left) >> 1) + left; + // 目标元素在前半段 + if (target >= arr[0]) { + // 如果当前元素也处于前半段 + if (arr[mid] >= arr[0]) { + // 如果当前元素大于目标元素, 往前找 + if (arr[mid] > target) { + right = mid - 1; + // 小于则往后找 + } else if (arr[mid] < target) { + left = mid + 1; + } else { + // 或者等于 + return mid; + } + // 如果当前元素处于后半段 + } else if (arr[mid] < arr[0]) { + // 往前找即可 + right = mid - 1; + } + } else { + // 目标元素在后半段 + // 如果当前元素也处于后半段 + if (arr[mid] < arr[0]) { + // 如果当前元素大于目标元素 往前找 + if (arr[mid] > target) { + right = mid - 1; + // 小于目标元素 往后找 + } else if (arr[mid] < target) { + left = mid + 1; + } else { + // 否则等于 返回坐标 + return mid; + } + //如果当前元素在前半段 直接往后找 + } else if (arr[mid] >= arr[0]) { + left = mid + 1; + } + } + } + return -1; + } +} From 384c4c3bccace9371277fcc75e44f4889fb0fa24 Mon Sep 17 00:00:00 2001 From: longwu Date: Tue, 25 Feb 2020 23:43:14 +0800 Subject: [PATCH 62/74] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=9C=89=E5=BA=8F?= =?UTF-8?q?=E6=88=AA=E6=96=AD=E6=95=B0=E7=BB=84=E7=9A=84=E6=9F=A5=E6=89=BE?= =?UTF-8?q?=E9=97=AE=E9=A2=98,=E6=89=BE=E6=9C=80=E5=B0=8F=E5=80=BC?= =?UTF-8?q?=E5=92=8C=E7=9B=AE=E6=A0=87=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/study/sort/MergeSort.java | 176 ++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 src/main/java/com/study/sort/MergeSort.java diff --git a/src/main/java/com/study/sort/MergeSort.java b/src/main/java/com/study/sort/MergeSort.java new file mode 100644 index 0000000..54ffd38 --- /dev/null +++ b/src/main/java/com/study/sort/MergeSort.java @@ -0,0 +1,176 @@ +package com.study.sort; + + +/** + * 归并排序, 节省内存空间 直接在原有的数组内部直接排序, 需要额外借助一个数组存放临时排序的元素 + * + * 时间复杂度 O(NlogN) + * + * 搜先通过不停的左右二分,将数组分成了类似二叉树的结构, + * 其递归的方式类似 二叉树的 后续遍历 左 右 根, 合并处理在根阶段 + * 使用数组5, 4, 7, 9, 3, 8, 2, 1进行举例 + * 根节点为完整数组, 一级子节点为 5479 和 3821 二级节点为 54,79 ,38,21 三级节点也就是叶子节点为 5,4,7,9, 3,8,2,1 + * 然后通过最底层左边5,4开始进行merge,得到4,5, 再右边7,9得到7,9, 再往上回溯 左边54和右边79合并得到5479, + * 然后是根的右边最底层3和8合并得到38,21合并得到12, 往上回溯38和12合并得到1238, + * 最后是 左5479节点和右1238合并得到根节点12345789 + *

+ * https://www.jianshu.com/p/33cffa1ce613 + */ +public class MergeSort { + + public static void main(String[] args) { + int[] arr = {5, 4, 7, 9, 3, 8, 2, 1}; + +// mergeSort(arr); +// print(arr); + + mergeSort_2(arr); + print(arr); + } + + private static void mergeSort(int[] arr) { + int[] tmp = new int[arr.length];//在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间 + sort(arr, 0, arr.length - 1, tmp); + } + + /** + * 搜先通过不停的左右二分,将数组分成了类似二叉树的结构, + * 其递归的方式类似 二叉树的 后续遍历 左 右 根, 合并处理在根阶段 + * 根节点为完整数组, 一级子节点为 5479 和 3821 二级节点为 54,79 ,38,21 三级节点也就是叶子节点为 5,4,7,9, 3,8,2,1 + * 然后通过最底层左边5,4开始进行merge,得到4,5, 再右边7,9得到7,9, 再往上回溯 左边54和右边79合并得到5479, + * 然后是根的右边最底层3和8合并得到38,21合并得到12, 往上回溯38和12合并得到1238, + * 最后是 左5479节点和右1238合并得到根节点12345789 + * + * @param arr + * @param left + * @param right + * @param tmp + */ + private static void sort(int[] arr, int left, int right, int[] tmp) { + if (left < right) { + int mid = left + ((right - left) >> 1); + + // 将数组不断的分成左右两部分, 直到分成无数个单个元素数组后,进行merge排序 + + // 左边分到不能再分 就进行回溯, 回溯的过程中再进行右边分 + sort(arr, left, mid, tmp); // 左边归并排序,使得左子序列有序 + sort(arr, mid + 1, right, tmp); // 右边归并排序, 使得右子序列有序 + + String s1 = ""; + for (int i = left; i <= mid; i++) + s1 += arr[i] + " "; + String s2 = ""; + for (int j = mid + 1; j <= right; j++) + s2 += arr[j] + " "; + + System.out.println(String.format("分成了数组%s和数组%s", s1, s2)); + // 每次左右两部分排序完后 再进行合并 + merge(arr, left, mid, right, tmp); //将两个有序子数组合并操作 + String s = ""; + for (int a = left; a <= right; a++) + s += arr[a]; + System.out.println("合并后得到: " + s); + } + } + + /** + * 将arr中的 left到mid, mid+1到right 两部分进行合并 + * + * @param arr + * @param left + * @param mid + * @param right + * @param tmp + */ + private static void merge(int[] arr, int left, int mid, int right, int[] tmp) { + int i = left; // 左边数组开始指针 + int j = mid + 1;// 右边数组开始指针 + int t = 0; // 临时数组开始指针 + + // 将左边和右边数组里的元素 从小到大排放入临时数组 + while (i <= mid && j <= right) { + if (arr[i] <= arr[j]) { + tmp[t++] = arr[i++]; + } else { + tmp[t++] = arr[j++]; + } + } + + // 上面循环执行完后, 左右数组里可能有一个数组还有元素没放入临时数组, 需要再次处理一下 + // 处理左边 + while (i <= mid) { + tmp[t++] = arr[i++]; + } + + // 处理右边 + while (j <= right) { + tmp[t++] = arr[j++]; + } + + // 最后将临时数组中合并好的元素重新放回原来的数组 + t = 0;//每次合并排序的元素放入tmp中都是从0开始的,所以取出也从0开始 + while (left <= right) { + arr[left++] = tmp[t++];//将tmp中的元素依次放回数组的left到right位置 + } + } + + private static void print(int[] arr) { + for (int e : arr) { + System.out.print(e + " "); + } + System.out.println(); + } + + private static void mergeSort_2(int[] arr) { + int[] tmp = new int[arr.length]; + sort_2(arr, 0, arr.length - 1, tmp); + } + + private static void sort_2(int[] arr, int left, int right, int[] tmp) { + // 左右两个数组中的元素不能相同 + if (left >= right) + return; + + int mid = ((right - left) >> 1) + left; + // 分治递归, 将数组不断分成单个元素的左右数组 + sort_2(arr, left, mid, tmp); + sort_2(arr, mid + 1, right, tmp); + + // 将左右数组进行排序合并, 合并后回溯到上层大数组再进行排序合并,最终完成整个数组排序 + merge_2(arr, left, mid, right, tmp); + } + + // 将左右两块数组进行合并 + // 将arr中的left 到 mid, mid+1 到 right 两部分数组进行合并排序人放入tmp中 + // 最后再从tmp中取出放回arr中 + private static void merge_2(int[] arr, int left, int mid, int right, int[] tmp) { + int i = left; + int j = mid + 1; + int t = 0; + + // 左边数组范围 left 到mid, 右边数组范围 mid+1到right + // 将元素从小到大放入tmp中 + while (i <= mid && j <= right) { + if (arr[i] >= arr[j]) { + tmp[t++] = arr[j++]; + } else { + tmp[t++] = arr[i++]; + } + } + + // 将左右数组中没有取出的元素放入tmp中 + while (i <= mid) { + tmp[t++] = arr[i++]; + } + + while (j <= right) { + tmp[t++] = arr[j++]; + } + + // 将tmp中的元素放回arr中 + t = 0; + while (left <= right) { + arr[left++] = tmp[t++]; + } + } +} From 7c66b35a791cd27cfad0e50aac15dabc657b22ed Mon Sep 17 00:00:00 2001 From: longwu Date: Sat, 29 Feb 2020 21:53:44 +0800 Subject: [PATCH 63/74] =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=9C=80=E5=90=8E?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E5=8D=95=E8=AF=8D=E7=9A=84=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E6=95=B0=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/string/LastWordCount.java | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/main/java/com/study/string/LastWordCount.java diff --git a/src/main/java/com/study/string/LastWordCount.java b/src/main/java/com/study/string/LastWordCount.java new file mode 100644 index 0000000..966eb26 --- /dev/null +++ b/src/main/java/com/study/string/LastWordCount.java @@ -0,0 +1,84 @@ +package com.study.string; + +import java.util.Scanner; + +/** + * 获取控制台输入的一行字符串, 取最后一个单词的长度 + * + * 比如hello jack, 输出4 + */ +public class LastWordCount { + + public static void main(String[] args) { + String input = "hello jack"; + //System.out.println(getLastWordCount(input)); + //System.out.println(getLastWordCount2(input)); + System.out.println(getLastWordCount3(input)); + + Scanner scan = new Scanner(System.in);// 获取控制台的输入 + // 输出敲回车之前的所有内容 + while (scan.hasNext()) { + //System.out.println(scan.next()); + System.out.println(getLastWordCount(scan.nextLine())); // 输入整行内容 + //System.out.println(getLastWordCount(scan.next()));// 将输入的内容按空格分割 + } + } + + /** + * 获取最后一个单词的长度, 将字符串反向遍历 + * @param input + * @return + */ + private static int getLastWordCount(String input) { + if (input == null || input.length() == 0) + return 0; + int count = 0; + int length = input.length(); + // 将字符串从后往前计算count, 当遇到空格的时候, 中断统计 + char[] chars = input.toCharArray(); + for (int i = length - 1; i >= 0; i--) { + if (chars[i] == ' ') + break; + count++; + } + return count; + } + + /** + * 从后往前遍历 + * @param input + * @return + */ + private static int getLastWordCount2(String input) { + if (input == null || input.length() == 0) + return 0; + int count = 0; + int length = input.length(); + // 将字符串从后往前计算count, 当遇到空格的时候, 中断统计 + for (int i = length - 1; i >= 0; i--) { + if (input.charAt(i) == ' ') + break; + count++; + } + return count; + } + + /** + * 按空格号分割字符串, 将字符串正序遍历 + * + * @param input + * @return + */ + private static int getLastWordCount3(String input) { + if (input == null || input.length() == 0) + return 0; + int count = 0; + for (int i = 0; i < input.length(); i++) { + count++; + if (input.charAt(i) == ' ') { + count = 0; // 每次遇到空格 都将count清零, 只有最后一个单词没有空格不会被清零 + } + } + return count; + } +} From c5aa99483d0e83258675d11c801bf1b29af805ad Mon Sep 17 00:00:00 2001 From: longwu Date: Sat, 29 Feb 2020 23:33:27 +0800 Subject: [PATCH 64/74] =?UTF-8?q?=E5=A4=84=E7=90=86=E6=95=B0=E7=BB=84?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E6=97=8B=E8=BD=AC=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/study/search/FindSmall.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/study/search/FindSmall.java b/src/main/java/com/study/search/FindSmall.java index 223f99c..e834615 100644 --- a/src/main/java/com/study/search/FindSmall.java +++ b/src/main/java/com/study/search/FindSmall.java @@ -13,6 +13,9 @@ *

* 问题三: 给定任意一个序列,找出其中的一个谷/峰,谷的定义为两边的数均大于某个数。 *

+ * + * https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/ + * * https://segmentfault.com/q/1010000000181714 *

* https://blog.csdn.net/weixin_34018169/article/details/89698260?utm_source=distribute.pc_relevant.none-task @@ -90,7 +93,11 @@ private static int findMin(int[] arr) { int left = 0, right = arr.length - 1; if (arr.length == 1) - return arr[0]; + return arr[left]; + + // 改进, leetcode上面还需要处理数组没有旋转的情况 + if(arr[left] <= arr[right]) + return arr[left]; // 找到目标为前面一个大于后面 while (left <= right) { @@ -125,8 +132,14 @@ public static int findMin2(int nums[]) { // 如果只有一个元素,直接返回 if (nums.length == 1) return nums[0]; + int result = nums[0]; int low = 0, high = nums.length - 1; + + // 改进, leetcode上面还需要处理数组没有旋转的情况 + if(nums[low] <= nums[high]) + return nums[low]; + int mid; // 确保 low 下标对应的值在左边的递增子数组,high 对应的值在右边递增子数组 while (nums[low] >= nums[high]) { From d732bb1970e371e8828037b02432c1c90244b5fa Mon Sep 17 00:00:00 2001 From: longwu Date: Sun, 1 Mar 2020 16:21:13 +0800 Subject: [PATCH 65/74] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E6=A0=91=E6=9F=A5=E6=89=BE=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../binarytree/binarysearchtree/Search.java | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/main/java/com/study/binarytree/binarysearchtree/Search.java diff --git a/src/main/java/com/study/binarytree/binarysearchtree/Search.java b/src/main/java/com/study/binarytree/binarysearchtree/Search.java new file mode 100644 index 0000000..7b566eb --- /dev/null +++ b/src/main/java/com/study/binarytree/binarysearchtree/Search.java @@ -0,0 +1,116 @@ +package com.study.binarytree.binarysearchtree; + + +import com.study.binarytree.TreeNode; +import com.study.utils.TreeUtils; + +import java.util.List; + +/** + * 查找二叉搜索树的某个节点 + */ +public class Search { + + public static void main(String[] args) { + // 由于二叉搜索树也是二叉树, 所以既可以使用二叉搜索树的方法也可以使用二叉树的方法求最近公共祖先 + int[] arr = {5, 3, 7, 1, 4, 6, 8}; + List treeNodes = TreeUtils.buildTreeAndList(arr); + TreeNode root = treeNodes.get(0); + + TreeUtils.show(root); + + //int target = 6; + int target = 2; + //boolean result = searchByRecursion(root,target); + //TreeNode result = searchByRecursion2(root, target); + TreeNode result = searchByLoop(root, target); + System.out.println(result != null ? result.val : "未找到"); + } + + /** + * 后续遍历查找结果, 需要在递归的时候将参数传进去 + * + * @param root 根节点 + * @param target 目标值 + * @return + */ + private static boolean searchByRecursion(TreeNode root, int target) { + if (root == null) + return false; + + if (root.val > target) { + return searchByRecursion(root.left, target); + } + + if (root.val < target) { + return searchByRecursion(root.right, target); + } + + // 后续遍历 查找结果 + return true; + } + + /** + * 先序遍历查找目标节点 + * + * @param root + * @param target + * @return + */ + private static TreeNode searchByRecursion2(TreeNode root, int target) { + // 如果为空, 说明递归到了最底层 叶子节点也没找到 + if (root == null) { + System.out.println("没有找到目标节点"); + return null; + } + + // 如果找到了目标 + if (root.val == target) { + // 一旦找到, 返回结果 + System.out.println("找到了节点" + root.val); + System.out.println("开始回溯"); + return root; + } + + // 如果目标值小于根节点, 往左子树找 + if (root.val > target) { + // 找到目标节点后, 如果曾经从左边找过, 会从左边回溯,并返回结果 + System.out.println("从左子树节点找" + root.left.val); + TreeNode left = searchByRecursion2(root.left, target); + System.out.println("回溯到左子树节点" + root.left.val); + return left; + } else { + // 如果目标值大于根节点, 往右子树找 + System.out.println("从右子树节点找" + root.right.val); + TreeNode right = searchByRecursion2(root.right, target); + System.out.println("回溯到右子树节点" + root.right.val); + return right; + } + } + + /** + * 使用循环的方式,查找目标节点 + * + * @param root + * @param target + * @return + */ + private static TreeNode searchByLoop(TreeNode root, int target) { + while (root != null) { + // 找到目标节点 直接返回 + if (root.val == target) + return root; + + // 目标节点小于根节点 + if (root.val > target) { + // 往左找 + root = root.left; + } else { + // 大于目标节点往右找 + root = root.right; + } + } + // 循环结束的时候,还没找到 返回空 + return null; + } +} From 426d5007515212c165188c2c0e6c68481ca56ac1 Mon Sep 17 00:00:00 2001 From: longwu Date: Sun, 1 Mar 2020 23:10:50 +0800 Subject: [PATCH 66/74] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E6=A0=91=E6=8F=92=E5=85=A5=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../binarytree/binarysearchtree/CRUD.java | 236 ++++++++++++++++++ .../binarytree/binarysearchtree/Search.java | 116 --------- 2 files changed, 236 insertions(+), 116 deletions(-) create mode 100644 src/main/java/com/study/binarytree/binarysearchtree/CRUD.java delete mode 100644 src/main/java/com/study/binarytree/binarysearchtree/Search.java diff --git a/src/main/java/com/study/binarytree/binarysearchtree/CRUD.java b/src/main/java/com/study/binarytree/binarysearchtree/CRUD.java new file mode 100644 index 0000000..2df6be8 --- /dev/null +++ b/src/main/java/com/study/binarytree/binarysearchtree/CRUD.java @@ -0,0 +1,236 @@ +package com.study.binarytree.binarysearchtree; + + +import com.study.binarytree.TreeNode; +import com.study.utils.TreeUtils; + +import java.util.List; + +/** + * 查找二叉搜索树的某个节点 + */ +public class CRUD { + + public static void main(String[] args) { + // 由于二叉搜索树也是二叉树, 所以既可以使用二叉搜索树的方法也可以使用二叉树的方法求最近公共祖先 + int[] arr = {5, 3, 8, 1, 4, 7, 9}; + List treeNodes = TreeUtils.buildTreeAndList(arr); + TreeNode root = treeNodes.get(0); + + TreeUtils.show(root); + + int target = 7; + //int target = 2; + //boolean result = searchByRecursion(root,target); + TreeNode result = searchByRecursion2(root, target); + //TreeNode result = searchByLoop(root, target); + //System.out.println(result != null ? result.val : "未找到"); + + TreeUtils.show(root); + //TreeNode node = insertByRecursion(root, 6); + //TreeNode node = insertByRecursion2(root, 6); + TreeNode node = insertByLoop(root, 6); + System.out.println(node.val); + TreeUtils.show(root); + } + + /** + * 后续遍历查找结果, 需要在递归的时候将参数传进去 + * + * @param root 根节点 + * @param target 目标值 + * @return 是否找到目标 + */ + private static boolean searchByRecursion(TreeNode root, int target) { + if (root == null) { + return false; // 未找到目标 + } + + if (root.val > target) { + // 将找到或者未找到目标不断回溯上去 + return searchByRecursion(root.left, target); + } + + if (root.val < target) { + // 将找到或者未找到目标不断回溯上去 + return searchByRecursion(root.right, target); + } + + // 找到目标 + return true; + } + + /** + * 查找目标节点 + * + * @param root 根节点 + * @param target 目标值 + * @return 目标节点 + */ + private static TreeNode searchByRecursion2(TreeNode root, int target) { + if (root == null) + return null; + + if (root.val > target) { + return searchByRecursion2(root.left, target); + } + + if (root.val < target) { + return searchByRecursion2(root.right, target); + } + + // 后续遍历 查找结果 + return root; + } + + /** + * 先序遍历查找目标节点 + * + * @param root + * @param target + * @return + */ + private static TreeNode searchByRecursion3(TreeNode root, int target) { + // 如果为空, 说明递归到了最底层 叶子节点也没找到 + if (root == null) { + System.out.println("没有找到目标节点"); + return null; + } + + // 如果找到了目标 + if (root.val == target) { + // 一旦找到, 返回结果 + System.out.println("找到了节点" + root.val); + System.out.println("开始回溯"); + return root; + } + + // 如果目标值小于根节点, 往左子树找 + if (root.val > target) { + // 找到目标节点后, 如果曾经从左边找过, 会从左边回溯,并返回结果 + System.out.println("从左子树节点找" + root.left.val); + TreeNode left = searchByRecursion2(root.left, target); + System.out.println("回溯到左子树节点" + root.left.val); + return left; + } else { + // 如果目标值大于根节点, 往右子树找 + System.out.println("从右子树节点找" + root.right.val); + TreeNode right = searchByRecursion2(root.right, target); + System.out.println("回溯到右子树节点" + root.right.val); + return right; + } + } + + /** + * 使用循环的方式,查找目标节点 + * + * @param root + * @param target + * @return + */ + private static TreeNode searchByLoop(TreeNode root, int target) { + while (root != null) { + // 找到目标节点 直接返回 + if (root.val == target) + return root; + + // 目标节点小于根节点 + if (root.val > target) { + // 往左找 + root = root.left; + } else { + // 大于目标节点往右找 + root = root.right; + } + } + // 循环结束的时候,还没找到 返回空 + return null; + } + + /** + * 往树中插入新节点 + *

+ * 首先通过二分查找 找到该节点的位置, 如果节点存在, 直接返回, 如果不存在则创建并返回. + * + * @param root 根节点 + * @param value 要插入的节点 + * @return 根节点 + */ + private static TreeNode insertByRecursion(TreeNode root, int value) { + if (root == null) { + // 如果没找到, 创建该节点 + return new TreeNode(value); + } + + // 从左边找 + if (root.val > value) { + // 将返回的左节点添加到根节点左边, 如果创建了一个新的节点,将被接上 + root.left = insertByRecursion(root.left, value); + } + + if (root.val < value) { + // 从右边找 + // 将返回的右节点添加到根节点右边, 如果创建了一个新的节点,将被接上 + root.right = insertByRecursion(root.right, value); + } + + // 不断回溯到根节点 + return root; + } + + private static TreeNode insertByRecursion2(TreeNode root, int value) { + // 没找到就创建目标节点并返回 + if (root == null) { + return new TreeNode(value); + } + + if (root.val > value) { + // 从左边找 + root.left = insertByRecursion2(root.left, value); + } + if (root.val < value) { + // 从右边找 + root.right = insertByRecursion2(root.right, value); + } + + return root; + } + + /** + * 通过迭代的方式插入新节点 + * + * @param root 根 + * @param val 新节点值 + * @return 根节点 + */ + private static TreeNode insertByLoop(TreeNode root, int val) { + TreeNode result = root; + while (root != null) { + if (root.val > val) { + // 判断左节点是否为空, 为空就说明新节点不存在,则创建 + if (root.left == null) { + root.left = new TreeNode(val); + break; + } + // 往左边找 + root = root.left; + } + if (root.val < val) { + // 判断右节点是否为空, 为空就说明新节点不存在,则创建 + if (root.right == null) { + root.right = new TreeNode(val); + break; + } + // 往右边找 + root = root.right; + } + // 新节点已经存在,停止查找,无需新建 + if (root.val == val) + break; + } + + // 返回根节点 + return result; + } + +} diff --git a/src/main/java/com/study/binarytree/binarysearchtree/Search.java b/src/main/java/com/study/binarytree/binarysearchtree/Search.java deleted file mode 100644 index 7b566eb..0000000 --- a/src/main/java/com/study/binarytree/binarysearchtree/Search.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.study.binarytree.binarysearchtree; - - -import com.study.binarytree.TreeNode; -import com.study.utils.TreeUtils; - -import java.util.List; - -/** - * 查找二叉搜索树的某个节点 - */ -public class Search { - - public static void main(String[] args) { - // 由于二叉搜索树也是二叉树, 所以既可以使用二叉搜索树的方法也可以使用二叉树的方法求最近公共祖先 - int[] arr = {5, 3, 7, 1, 4, 6, 8}; - List treeNodes = TreeUtils.buildTreeAndList(arr); - TreeNode root = treeNodes.get(0); - - TreeUtils.show(root); - - //int target = 6; - int target = 2; - //boolean result = searchByRecursion(root,target); - //TreeNode result = searchByRecursion2(root, target); - TreeNode result = searchByLoop(root, target); - System.out.println(result != null ? result.val : "未找到"); - } - - /** - * 后续遍历查找结果, 需要在递归的时候将参数传进去 - * - * @param root 根节点 - * @param target 目标值 - * @return - */ - private static boolean searchByRecursion(TreeNode root, int target) { - if (root == null) - return false; - - if (root.val > target) { - return searchByRecursion(root.left, target); - } - - if (root.val < target) { - return searchByRecursion(root.right, target); - } - - // 后续遍历 查找结果 - return true; - } - - /** - * 先序遍历查找目标节点 - * - * @param root - * @param target - * @return - */ - private static TreeNode searchByRecursion2(TreeNode root, int target) { - // 如果为空, 说明递归到了最底层 叶子节点也没找到 - if (root == null) { - System.out.println("没有找到目标节点"); - return null; - } - - // 如果找到了目标 - if (root.val == target) { - // 一旦找到, 返回结果 - System.out.println("找到了节点" + root.val); - System.out.println("开始回溯"); - return root; - } - - // 如果目标值小于根节点, 往左子树找 - if (root.val > target) { - // 找到目标节点后, 如果曾经从左边找过, 会从左边回溯,并返回结果 - System.out.println("从左子树节点找" + root.left.val); - TreeNode left = searchByRecursion2(root.left, target); - System.out.println("回溯到左子树节点" + root.left.val); - return left; - } else { - // 如果目标值大于根节点, 往右子树找 - System.out.println("从右子树节点找" + root.right.val); - TreeNode right = searchByRecursion2(root.right, target); - System.out.println("回溯到右子树节点" + root.right.val); - return right; - } - } - - /** - * 使用循环的方式,查找目标节点 - * - * @param root - * @param target - * @return - */ - private static TreeNode searchByLoop(TreeNode root, int target) { - while (root != null) { - // 找到目标节点 直接返回 - if (root.val == target) - return root; - - // 目标节点小于根节点 - if (root.val > target) { - // 往左找 - root = root.left; - } else { - // 大于目标节点往右找 - root = root.right; - } - } - // 循环结束的时候,还没找到 返回空 - return null; - } -} From 245dac87b5efe1b95bdfdaca379e1b96249a573f Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 2 Mar 2020 00:23:51 +0800 Subject: [PATCH 67/74] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=A0=91=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=9B=AE=E5=BD=95=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/binarytree/BalancedBinaryTree.java | 144 ----------- .../study/binarytree/InorderTraversal.java | 159 ------------ .../java/com/study/binarytree/InvertTree.java | 103 -------- .../com/study/binarytree/LevelTraversal.java | 184 ------------- .../com/study/binarytree/LevelTraversal2.java | 82 ------ .../binarytree/LowestCommonAncestor.java | 123 --------- .../com/study/binarytree/MaximumDepth.java | 132 ---------- .../com/study/binarytree/MinimumDepth.java | 127 --------- .../com/study/binarytree/NodesInALevel.java | 165 ------------ .../study/binarytree/PostorderTraversal.java | 242 ------------------ .../study/binarytree/PreorderTraversal.java | 174 ------------- .../com/study/binarytree/TraverseTree.java | 145 ----------- .../java/com/study/binarytree/TreeNode.java | 11 - .../java/com/study/binarytree/TreeNode2.java | 11 - .../binarytree/binarysearchtree/CRUD.java | 236 ----------------- .../LowestCommonAncestor.java | 109 -------- .../ValidateBinarySearchTree.java | 237 ----------------- .../ntree/MaximumDepthOfNAryTree.java | 2 +- .../com/study/{ => tree}/ntree/NTreeNode.java | 2 +- src/main/java/com/study/trietree/Trie.java | 231 ----------------- .../java/com/study/trietree/TrieNode.java | 27 -- 21 files changed, 2 insertions(+), 2644 deletions(-) delete mode 100644 src/main/java/com/study/binarytree/BalancedBinaryTree.java delete mode 100644 src/main/java/com/study/binarytree/InorderTraversal.java delete mode 100644 src/main/java/com/study/binarytree/InvertTree.java delete mode 100644 src/main/java/com/study/binarytree/LevelTraversal.java delete mode 100644 src/main/java/com/study/binarytree/LevelTraversal2.java delete mode 100644 src/main/java/com/study/binarytree/LowestCommonAncestor.java delete mode 100644 src/main/java/com/study/binarytree/MaximumDepth.java delete mode 100644 src/main/java/com/study/binarytree/MinimumDepth.java delete mode 100644 src/main/java/com/study/binarytree/NodesInALevel.java delete mode 100644 src/main/java/com/study/binarytree/PostorderTraversal.java delete mode 100644 src/main/java/com/study/binarytree/PreorderTraversal.java delete mode 100644 src/main/java/com/study/binarytree/TraverseTree.java delete mode 100644 src/main/java/com/study/binarytree/TreeNode.java delete mode 100644 src/main/java/com/study/binarytree/TreeNode2.java delete mode 100644 src/main/java/com/study/binarytree/binarysearchtree/CRUD.java delete mode 100644 src/main/java/com/study/binarytree/binarysearchtree/LowestCommonAncestor.java delete mode 100644 src/main/java/com/study/binarytree/binarysearchtree/ValidateBinarySearchTree.java rename src/main/java/com/study/{ => tree}/ntree/MaximumDepthOfNAryTree.java (97%) rename src/main/java/com/study/{ => tree}/ntree/NTreeNode.java (92%) delete mode 100644 src/main/java/com/study/trietree/Trie.java delete mode 100644 src/main/java/com/study/trietree/TrieNode.java diff --git a/src/main/java/com/study/binarytree/BalancedBinaryTree.java b/src/main/java/com/study/binarytree/BalancedBinaryTree.java deleted file mode 100644 index 4961c5d..0000000 --- a/src/main/java/com/study/binarytree/BalancedBinaryTree.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.study.binarytree; - -import com.study.utils.TreeUtils; - -/** - * 验证平衡二叉树 - * - * 给定一个二叉树,判断它是否是高度平衡的二叉树。 - *

- * 本题中,一棵高度平衡二叉树定义为: - *

- * 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。 - *

- * 示例 1: - *

- * 给定二叉树 [3,9,20,null,null,15,7] - *

- * 3 - * / \ - * 9 20 - * / \ - * 15 7 - * 返回 true 。 - *

- * 示例 2: - *

- * 给定二叉树 [1,2,2,3,3,null,null,4,4] - *

- * 1 - * / \ - * 2 2 - * / \ - * 3 3 - * / \ - * 4 4 - * 返回 false 。 - *

- * https://leetcode-cn.com/problems/balanced-binary-tree/ - */ -public class BalancedBinaryTree { - - public static void main(String[] args) { - //Integer[] nodes = {3, 9, 20, null, null, 15, 7}; - Integer[] nodes = {1, 2, 2, 3, 3, null, null, 4, 4}; - TreeNode root = TreeUtils.buildTree(nodes); - TreeUtils.show(root); - - //System.out.println(isBalanced(root)); - System.out.println(isBalanced2(root)); - } - - /** - * 使用前序遍历(根左右),先处理根节点获取高度, 再遍历左右子树是否为平衡二叉树. 从顶至底(暴力法) - * 通过比较左右子树最大高度差是否大于1 来判断以此节点为根节点下是否是二叉平衡树 - *

- * 从顶至底DFS,以每个节点为根节点,递归判断是否是平衡二叉树: - * 若所有根节点都满足平衡二叉树性质,则返回 True ; - * 若其中任何一个节点作为根节点时,不满足平衡二叉树性质,则返回False。 - *

- * 最差时间复杂度为O(N^2) - *

- * 作者:jyd - * 链接:https://leetcode-cn.com/problems/balanced-binary-tree/solution/balanced-binary-tree-di-gui-fang-fa-by-jin40789108/ - * - * @param root - * @return - */ - public static boolean isBalanced(TreeNode root) { - // 根节点为空, 说明是空树,肯定是平衡的 - if (root == null) - return true; - - // 先处理根节点, 获取高度 - // 获取每个节点的左右子树的高度 - int left = getHeight(root.left); - int right = getHeight(root.right); - - // 判断当前root节点为根的树是否为平衡二叉树 - if (Math.abs(left - right) > 1) - return false; - - // 再进行左右子树遍历 - // 如果当前树为平衡树, 那么继续判断递归当前树的左右子树是否为平衡二叉树 - return isBalanced(root.left) && isBalanced(root.right); - } - - /** - * 获取一颗树的最大高度 - * - * @param root - * @return - */ - private static int getHeight(TreeNode root) { - if (root == null) - return 0; - - int left = getHeight(root.left); - - int right = getHeight(root.right); - - return Math.max(left, right) + 1; - } - - /** - * 后续遍历(左右根), 先获取左右子树的高度, 再作判断是否为平衡二叉树 - * 从底部到顶部, 获取每个节点为root根时候的子树最大高度差是否大于1(不平衡), 如果是返回-1; 否则返回当前子树的最大高度 - * - * 时间复杂度为O(n) - * - * https://leetcode-cn.com/problems/balanced-binary-tree/solution/balanced-binary-tree-di-gui-fang-fa-by-jin40789108/ - * - * @return - */ - private static boolean isBalanced2(TreeNode root) { - return depth(root) != -1; - } - - /** - * 后续遍历 - * @param root - * @return - */ - private static int depth(TreeNode root) { - if (root == null) - return 0; - - // 左子树出现不满足条件的 - int left = depth(root.left); - if (left == -1) - return -1; - - // 右子树出现不满足条件的 - int right = depth(root.right); - if (right == -1) - return -1; - - // 检查是否满足条件 - if (Math.abs(left - right) > 1) - return -1; - - // 返回当前左右子树的最大高都 - return Math.max(left, right) + 1; - } -} diff --git a/src/main/java/com/study/binarytree/InorderTraversal.java b/src/main/java/com/study/binarytree/InorderTraversal.java deleted file mode 100644 index c5641ae..0000000 --- a/src/main/java/com/study/binarytree/InorderTraversal.java +++ /dev/null @@ -1,159 +0,0 @@ -package com.study.binarytree; - -import com.study.utils.TreeUtils; - -import java.util.ArrayList; -import java.util.List; -import java.util.Stack; - -/** - * 94. 二叉树的中序遍历 - *

- * 给定一个二叉树,返回它的中序 遍历。 - *

- * 示例: - *

- * 输入: [1,null,2,3] - * 1 - * \ - * 2 - * / - * 3 - *

- * 输出: [1,3,2] - * 进阶: 递归算法很简单,你可以通过迭代算法完成吗? - *

- * https://leetcode-cn.com/problems/binary-tree-inorder-traversal/ - */ -public class InorderTraversal { - - public static void main(String[] args) { - Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - - TreeNode root = TreeUtils.buildTree(arr); - TreeUtils.show(root); - - //List list = inorderTraversal(root); - //List list = inorderTraversal2(root); - //List list = inorderTraversal3(root); - List list = inorderTraversal4(root); - for (Integer num : list) { - System.out.print(num + " "); - } - } - - /** - * 递归遍历 左 - 根 - 右 - * - * @param root - * @return - */ - private static List inorderTraversal(TreeNode root) { - List list = new ArrayList(); - traversal(root, list); - return list; - } - - private static void traversal(TreeNode root, List list) { - if (root != null) { - //递归左子树 - traversal(root.left, list); - list.add(root.val); - //递归右子树 - traversal(root.right, list); - } - } - - - /** - * 递归遍历2 左 - 根 - 右 - * - * @param root - * @return - */ - private static List inorderTraversal2(TreeNode root) { - List list = new ArrayList(); - if (root != null) { - // 递归左(右)子树 - if (root.left != null) { - list.addAll(inorderTraversal2(root.left)); - } - // 把某个根节点的值 加入到list中 - list.add(root.val); - // 递归右(左)子树 - if (root.right != null) { - list.addAll(inorderTraversal2(root.right)); - } - } - return list; - } - - /** - * 使用迭代法 进行中序遍历 - * - * @param root - * @return - */ - private static List inorderTraversal3(TreeNode root) { - List list = new ArrayList(); - - if (root == null) - return list; - - Stack stack = new Stack(); - - // 遍历条件为当前节点不为空 或者 栈不为空 - while (root != null || !stack.isEmpty()) { - // 将当前节点的所有左节点入栈 - // 如果当前节点不为空,递归其左节点 - while (root != null) { - stack.push(root);//将左节点入栈 - System.out.println(String.format("节点%d被放入栈中", root.val)); - root = root.left; - } - - // 从栈中弹出节点 - if (!stack.isEmpty()) { - // 将栈中的节点的第一个弹出 - // 存在左节点的,优先将最深层左节点弹出 - root = stack.pop(); - System.out.println(String.format("节点%d从栈中弹出", root.val)); - list.add(root.val); - System.out.println(String.format("将节点%d放入集合中", root.val)); - // 切到右子树,给下一轮找左子树使用 - root = root.right; - } - } - return list; - } - - private static List inorderTraversal4(TreeNode root) { - List list = new ArrayList<>(); - - if (root == null) - return list; - - Stack stack = new Stack<>(); - - // 中序遍历条件有两个 当前节点不为空 或者 栈不为空 - while (root != null || !stack.isEmpty()) { - - // 将当前节点的所有左边子节点入栈(包括根节点) - while (root != null) { - stack.push(root); - root = root.left; - } - - // 处理栈中的节点 - if (!stack.isEmpty()) { - // 将栈顶节点弹出并放入list - root = stack.pop(); - list.add(root.val); - - // 将节点切到弹出节点的右子节点, 便于下次迭代处理 - root = root.right; - } - } - return list; - } -} diff --git a/src/main/java/com/study/binarytree/InvertTree.java b/src/main/java/com/study/binarytree/InvertTree.java deleted file mode 100644 index 998cc99..0000000 --- a/src/main/java/com/study/binarytree/InvertTree.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.study.binarytree; - -import com.study.utils.TreeUtils; - -import java.util.LinkedList; -import java.util.Queue; - -/** - * 二叉树翻转 - *

- * 翻转一棵二叉树。 - *

- * 示例: - *

- * 输入: - *

- * 4 - * / \ - * 2 7 - * / \ / \ - * 1 3 6 9 - * 输出: - *

- * 4 - * / \ - * 7 2 - * / \ / \ - * 9 6 3 1 - *

- * 链接:https://leetcode-cn.com/problems/invert-binary-tree - */ -public class InvertTree { - - public static void main(String[] args) { - - Integer[] nodes = {4, 2, 7, 1, 3, 6, 9}; - TreeNode root = TreeUtils.buildTree(nodes); - TreeUtils.show(root); - //TreeNode newRoot = invertTree(root); - TreeNode newRoot = invertTree2(root); - TreeUtils.show(newRoot); - } - - - /** - * 后序遍历, 深度优先dfs 自下而上左右节点互换 - *

- * 时间复杂度O(n) - * - * @param root - * @return - */ - public static TreeNode invertTree(TreeNode root) { - if (root == null) - return null; - - TreeNode right = invertTree(root.right); - TreeNode left = invertTree(root.left); - - // 从最底层叶子节点开始左右互换 - root.left = right; - root.right = left; - - // 返回新的root节点,其左右子节点已经互换 - return root; - } - - /** - * 使用层级遍历的方式 - * - * 时间复杂度O(n) - * @param root - * @return - */ - public static TreeNode invertTree2(TreeNode root) { - if (root == null) - return null; - - Queue queue = new LinkedList(); - - queue.offer(root); - - // 一层一层遍历, 并将左右子节点互换 - while (!queue.isEmpty()) { - int size = queue.size(); - // 当前层级的所有节点取出, 并将其左右子节点进行互换 - for (int i = 0; i < size; i++) { - TreeNode node = queue.poll(); - if (node != null) { - TreeNode tmp = node.left; - node.left = node.right; - node.right = tmp; - - if (node.left != null) - queue.offer(node.left); - if (node.right != null) - queue.offer(node.right); - } - } - } - return root; - } -} diff --git a/src/main/java/com/study/binarytree/LevelTraversal.java b/src/main/java/com/study/binarytree/LevelTraversal.java deleted file mode 100644 index 6097d5e..0000000 --- a/src/main/java/com/study/binarytree/LevelTraversal.java +++ /dev/null @@ -1,184 +0,0 @@ -package com.study.binarytree; - -import com.study.utils.TreeUtils; - -import java.util.*; - -/** - * 层级遍历二叉树,广度优先遍历bfs - * 1 - * 2 3 - * 4 5 6 7 - * https://leetcode-cn.com/problems/binary-tree-level-order-traversal/comments/ - *

- * 解题思路, - * 1.借用一个queue先进先出的结构来遍历二叉树 remove和add方法 - * 2.借用一个stack后进先出的接口来遍历二叉树 pop和push方法 - *

- * 用宽度优先搜索遍历来划分层次:[[1], [2, 3], [4, 5 , 6 ,7]]。 - * https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/er-cha-shu-de-ceng-ci-bian-li-by-leetcode/ - */ -public class LevelTraversal { - public static void main(String[] args) { - Integer[] arr = {1, 2, 3, 4, 5, 6, 7}; - TreeNode root = TreeUtils.buildTree(arr); - - //List> allNodes = levelOrder(root); - List> allNodes = levelOrder2(root); - - for (List nodesInALevel : allNodes) { - System.out.println(); - for (Integer node : nodesInALevel) { - System.out.print(node + " "); - } - } - } - - /** - * 返回二叉树的所有节点的值 - *

- * 使用迭代的方式 - *

- * 思路: 使用一个队列来装载二叉树的每一层节点, 通过先进先出的原理使得取出来的节点可以根据装入的节点顺序一样(根 左 右) - * - * @param root - * @return - */ - private static List> levelOrder(TreeNode root) { - // 创建一个2维的节点集合, 第1维度为层集合,第2维度为每层的节点集合 - List> levels = new ArrayList>(); - //如果根节点为空,直接返回空列表 - if (root == null) return levels; - - // 创建一个当前层的队列 - Queue queue = new LinkedList(); - queue.add(root); - - while (!queue.isEmpty()) { - // 往节点结合中添加当前层级的空节点集合 - List currentLevelNodes = new ArrayList<>(); - // 获取当前层次的节点个数 - int level_length = queue.size(); - // 遍历当前层的节点个数 - for (int i = 0; i < level_length; i++) { - TreeNode node = queue.remove();//取出并移除队列的第一个节点 根据先进先出原则, 取出的顺序为 根 左 右(根据添加时候的顺序一致) - - // 往节点集合中添加当前层级的节点 - currentLevelNodes.add(node.val); - - // 往当前层队列中添加当前层的子节点,用于下一次迭代处理 - if (node.left != null) - queue.add(node.left); - if (node.right != null) - queue.add(node.right); - } - levels.add(currentLevelNodes); - } - return levels; - } - - /** - * 使用 queue - * - * @param root - * @return - */ - private static List> levelOrder2(TreeNode root) { - List> nodes = new ArrayList>(); - - if (root == null) - return nodes; - - Queue queue = new LinkedList(); - queue.add(root); - - // 遍历队列中的节点,当前层里的所有节点 - while (queue.size() > 0) { - List currentLevelNodes = new ArrayList<>(); - - // 当前层节点数 - int nodeCount = queue.size(); - - // 遍历当前层所有的节点, 往节点结合中添加当前层所有节点值, 往队列中添加当前所有节点的子节点 - for (int i = 0; i < nodeCount; i++) { - // 添加当前层节点值 - TreeNode node = queue.remove(); - currentLevelNodes.add(node.val); - - if (node.left != null) - queue.add(node.left); - if (node.right != null) - queue.add(node.right); - } - nodes.add(currentLevelNodes); - } - return nodes; - } - - /** - * 使用递归的方式实现广度优先, 这里在递归的过程中用到了层级控制,如果集合当前层没有节点,就新建; 如果有节点, 就加到当前层里面 - * - * @param root - * @return - */ - private static List> searchByRecursion(TreeNode root) { - List> nodeList = new ArrayList<>(); - if (root == null) - return nodeList; - // 递归获取所有子节点 - search(root, 0, nodeList); - return nodeList; - } - - private static void search(TreeNode root, int level, List> nodeList) { - if (root == null) { - return; - } - // 集合的当前层没有节点, 就新建 - if (level >= nodeList.size()) { - List subList = new ArrayList<>(); - subList.add(root); - nodeList.add(subList); - } else { - // 集合的当前层有节点, 就直接添加 - nodeList.get(level).add(root); - } - search(root.left, level + 1, nodeList); - search(root.right, level + 1, nodeList); - } - - private static List> levelOrder3(TreeNode root) { - List> nodes = new ArrayList>(); - - if (root == null) - return nodes; - - int level = 0; - Queue queue = new LinkedList(); - queue.add(root); - - // 遍历队列中的节点,当前层里的所有节点 - while (queue.size() > 0) { - nodes.add(new ArrayList()); - - // 当前层节点数 - int nodeCount = queue.size(); - - // 遍历当前层所有的节点, 往节点结合中添加当前层所有节点值, 往队列中添加当前所有节点的子节点 - for (int i = 0; i < nodeCount; i++) { - // 添加当前层节点值 - TreeNode node = queue.remove(); - nodes.get(level).add(node.val); - - if (node.left != null) - queue.add(node.left); - if (node.right != null) - queue.add(node.right); - } - - // 接着循环下一层 - level++; - } - return nodes; - } -} diff --git a/src/main/java/com/study/binarytree/LevelTraversal2.java b/src/main/java/com/study/binarytree/LevelTraversal2.java deleted file mode 100644 index ba534fb..0000000 --- a/src/main/java/com/study/binarytree/LevelTraversal2.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.study.binarytree; - - -import com.study.utils.TreeUtils; -import sun.reflect.generics.tree.Tree; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; - - -/** - * 二叉树遍历2 - * - * 将一个二叉树各节点输出, 奇数行按顺序输出,偶数行倒叙输出 - * input: - * A - * / \ - * B C - * / \ \ - * D E F - * / \ - * G H - * output: - * A - * CB - * DEF - * HG - */ -public class LevelTraversal2 { - - public static void main(String[] args) { - String[] nodes = {"A", "B", "C", "D", "E", null, "F", null,null, "G", null, null, null, null, "H"}; - TreeNode2 root = TreeUtils.buildTree(nodes); - TreeUtils.show(root); - List> list = getTreeList(root); - - int level = 1; - for (List levelList : list) { - // 奇数行 正序输出 - if (level % 2 != 0) { - for (int i = 0; i < levelList.size(); i++) { - System.out.print(levelList.get(i)); - } - } else { - //偶数行 倒叙输出 - for (int i = levelList.size() - 1; i >= 0; i--) { - System.out.print(levelList.get(i)); - } - } - System.out.println(); - level++; - } - } - - private static List> getTreeList(TreeNode2 root) { - List> list = new ArrayList<>(); - - Queue queue = new LinkedList<>(); - queue.offer(root); - - while (!queue.isEmpty()) { - List levelList = new ArrayList<>(); - int size = queue.size(); - for (int i = 0; i < size; i++) { - TreeNode2 node = queue.poll(); - levelList.add(node.val); - - if (node.left != null) { - queue.offer(node.left); - } - if (node.right != null) { - queue.offer(node.right); - } - } - list.add(levelList); - } - - return list; - } -} diff --git a/src/main/java/com/study/binarytree/LowestCommonAncestor.java b/src/main/java/com/study/binarytree/LowestCommonAncestor.java deleted file mode 100644 index f763b54..0000000 --- a/src/main/java/com/study/binarytree/LowestCommonAncestor.java +++ /dev/null @@ -1,123 +0,0 @@ -package com.study.binarytree; - -import com.study.utils.TreeUtils; - -import java.util.ArrayList; -import java.util.List; - -/** - * 二叉树的最近公共祖先 - *

- * 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 - *

- * 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” - *

- * 例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4] - *

- * 示例 1: - *

- * 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 - * 输出: 3 - * 解释: 节点 5 和节点 1 的最近公共祖先是节点 3。 - * 示例 2: - *

- * 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 - * 输出: 5 - * 解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。 - *

- * 说明: - *

- * 所有节点的值都是唯一的。 - * p、q 为不同节点且均存在于给定的二叉树中。 - *

- * https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/ - *

- * https://time.geekbang.org/course/detail/130-42708 - */ -public class LowestCommonAncestor { - - public static void main(String[] args) { - int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - List treeNodes = TreeUtils.buildTreeAndList(arr); - //TreeNode p = treeNodes.get(4); - //TreeNode q = treeNodes.get(5); - -// TreeNode p = treeNodes.get(1); -// TreeNode q = treeNodes.get(2); - -// TreeNode p = treeNodes.get(3); -// TreeNode q = treeNodes.get(4); - - TreeNode p = treeNodes.get(1); - TreeNode q = treeNodes.get(7); - TreeNode root = treeNodes.get(0); - - TreeNode lca = getlca(root, p, q); - if (lca == null) { - System.out.println("没有最近公共祖先"); - } else { - System.out.println(lca.val); - } - } - - /** - * 递归的方式查找最近公共祖先 - *

- * 先从左子树进行查找,如果找到第一个目标节点p, 那么不再往下查找,而是直接从目标节点的父节点r的右子树进行查找 - *

- * 如果右子树也找到目标节点q,说明r就是p和q的最近公共祖先 - *

- * 如果右子树没有找到目标节点q, 那么目标节点q肯定在目标节点p的子节点中. 那么p就是p和q的最近公共祖先 - *

- * 如果左子树没有找到目标节点, 那么再从右子树找, 如果找到第一个目标节点q, 那么q就是p和q的最近公共祖先,因为p肯定是q的子节点 - *

- * 若果左右子树都没有找到目标节点, 那么直接返回null - * - * @param root - * @param p - * @param q - * @return - */ - private static TreeNode getlca(TreeNode root, TreeNode p, TreeNode q) { - if (root == null) { - System.out.println("递归往内中止返回null值"); - return null; - } - - if (root == p || root == q) { - if (root == p) - System.out.println("递归往内中止, root == p ==" + root.val); - if (root == q) - System.out.println("递归往内中止, root == q ==" + root.val); - return root; - } - - // 遍历左子树看有没有目标节点, 有返回目标节点, 没有返回null - TreeNode left = getlca(root.left, p, q); // 如果找到目标节点, 无需下遍历, 直接从当前节点的右子树进行遍历 - System.out.println("递归往外开始, 左边目标节点left = " + (left == null ? "null" : left.val)); - // 遍历右子树有看没有目标节点, 有返回目标节点, 没有返回null - TreeNode right = getlca(root.right, p, q); - System.out.println("递归往外开始, 右边目标节点right = " + (right == null ? "null" : right.val)); - - // 如果左右子树都有目标节点, 说明公共祖先就是它本身 - if (left != null && right != null) { - System.out.println("左右子树都有目标节点,公共祖先为" + root.val); - return root; - } - - // 如果目标节点都在左子树,返回左子树匹配的节点 - // 如果目标节点都在右子树,返回右子树匹配的节点 - //return left != null ? left : right; - - if (left != null) { - System.out.println("公共祖先在左子树, left = " + left.val); - return left; - } - if (right != null) { - System.out.println("公共祖先在右子树, right = " + left.val); - return right; - } - - return null; - } -} diff --git a/src/main/java/com/study/binarytree/MaximumDepth.java b/src/main/java/com/study/binarytree/MaximumDepth.java deleted file mode 100644 index 0dbed94..0000000 --- a/src/main/java/com/study/binarytree/MaximumDepth.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.study.binarytree; - -import com.study.utils.TreeUtils; - -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; - -/** - * 二叉树的最大深度 - *

- * 给定一个二叉树,找出其最大深度。 - *

- * 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 - *

- * 说明: 叶子节点是指没有子节点的节点。 - *

- * 示例: - * 给定二叉树 [3,9,20,null,null,15,7], - *

- * 3 - * / \ - * 9 20 - * / \ - * 15 7 - * 返回它的最大深度 3 。 - *

- * 链接:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree - */ -public class MaximumDepth { - - /** - * 可以通过深度优先进行递归 或者 层级遍历 - * - * @param args - */ - public static void main(String[] args) { - //Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - Integer[] arr = {5, 1, 4, null, null, 3, 6}; - TreeNode root = TreeUtils.buildTree(arr); - TreeUtils.show(root); - - System.out.println(maxDepthByRecursion(root, "根", root.val)); - //System.out.println(maxDepthByLoop(root)); - //System.out.println(maxDepthByRecursion2(root)); - } - - /** - * 使用dfs深度优先的策略(递归)的方式来解决这个问题 - *

- * 从根递归到最底层叶子节点, 值为1, 然后每往上面一层+1,同时每层会对左右两边的值做对比,取最大的. 如果是算最大深度,那就是取根的左子树和右子树中的最大值 - *

- * 我们每个结点只访问一次,因此时间复杂度为 O(N), - * - * @param root - * @return - */ - private static int maxDepthByRecursion(TreeNode root, String position, int val) { - if (root == null) { - System.out.println(String.format("%s从%d节点到叶子节点递归终止", position, val)); - return 0; - } - - System.out.println(String.format("%s从%d节点到叶子节点递归", position, val)); - - int left = maxDepthByRecursion(root.left, "左边", root.val); - // 左边递归到了叶子节点, 开始往根节点走 - System.out.println(String.format("左边从叶子节点往根节点开始, 当前节点为: %d left = %d", root.val, left)); - - int right = maxDepthByRecursion(root.right, "右边", root.val); - // 右边递归到了叶子节点, 开始往根节点走 - System.out.println(String.format("右边从叶子节点往根节点开始, 当前节点为: %d right = %d", root.val, right)); - - int result = Math.max(left, right) + 1; - System.out.println(String.format("result = %d, left = %d, right = %d", result, left, right)); - - return result; - } - - /** - * 深度优先遍历, 计算每层左右节点的层数,取最大值 - * - * 时间复杂度为O(n) - * - * @param root - * @return - */ - private static int maxDepthByRecursion2(TreeNode root) { - if (root == null) - return 0; - - int left = maxDepthByRecursion2(root.left); - int right = maxDepthByRecursion2(root.right); - - // 从最底层往根节点回溯的过程中, 计算每层的层数, 取左右两边最大的层数并+1(每层的层数) - // 每次回溯到上一层 都会将当前层左右节点的最大层数+1 带给上一层 - return Math.max(left, right) + 1;// 最底层为0+1=1, 倒数第2层为0+1+1=2 - } - - - /** - * 使用广度优先的方式找出最大深度 遍历+队列 - * 时间复杂度为O(n) - * - * @param root - * @return - */ - private static int maxDepthByLoop(TreeNode root) { - if (root == null) - return 0; - - Queue queue = new LinkedList<>(); - queue.offer(root); - int level = 0; - while (!queue.isEmpty()) { - int size = queue.size(); - - for (int i = 0; i < size; i++) { - TreeNode node = queue.poll(); - if (node.left != null) { - queue.offer(node.left); - } - if (node.right != null) { - queue.offer(node.right); - } - } - level++; - } - // 返回最大层级数, 即最深层级 - return level; - } -} diff --git a/src/main/java/com/study/binarytree/MinimumDepth.java b/src/main/java/com/study/binarytree/MinimumDepth.java deleted file mode 100644 index 22cf815..0000000 --- a/src/main/java/com/study/binarytree/MinimumDepth.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.study.binarytree; - -import com.study.utils.TreeUtils; -import sun.reflect.generics.tree.Tree; - -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; - -/** - * 二叉树的最小深度 - *

- * 给定一个二叉树,找出其最小深度。 - *

- * 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 - *

- * 说明: 叶子节点是指没有子节点的节点。 - *

- * 示例: - *

- * 给定二叉树 [3,9,20,null,null,15,7], - *

- * 3 - * / \ - * 9 20 - * / \ - * 15 7 - * 返回它的最小深度  2. - *

- * 链接:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree - */ -public class MinimumDepth { - - public static void main(String[] args) { - //Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - //Integer[] arr = {5, 1, 4, null, null, 3}; - Integer[] arr = {5, 1}; - TreeNode root = TreeUtils.buildTree(arr); - TreeUtils.show(root); - //System.out.println(minDepthByRecursion(root)); - System.out.println(minDepthByRecursion2(root)); - //System.out.println(minDepthByLoop(root)); - } - - /** - * 使用分治的方法 从左右节点进行递归, - * 注意: 当root节点左右孩子都为空时,返回1 - *

- * 如果是单边为空或者都为空的节点, 返回另一边+1 这样能够记录所有分支的高度, 最后通过min(跟的左分支,根的右分支) 得到深度最小的 - *

- * 时间复杂度为O(n) - * - * @param root - * @return - */ - private static int minDepthByRecursion(TreeNode root) { - if (root == null) - return 0; - // 使用分治的方法 从左右节点进行递归 - int left = minDepthByRecursion(root.left); - int right = minDepthByRecursion(root.right); - - // 如果是单边为空或者都为空的节点, 返回另一边+1 这样能够记录所有分支的高度, 最后通过min(跟的左分支,根的右分支) 得到深度最小的 - // 如果左边子节点为空的时候, 那么返回右边不为空的孩子深度+1 - if (left == 0) - return right + 1; - // 如果右边子节点为空的时候, 那么返回左边不为空的孩子深度+1 - else if (right == 0) - return left + 1; - else //如果左右子节点都不为空, 返回左右深度最小的那个+1 - return Math.min(left, right) + 1; - } - - - private static int minDepthByRecursion2(TreeNode root) { - if (root == null) - return 0; - - int left = minDepthByRecursion2(root.left); - int right = minDepthByRecursion2(root.right); - - // 记录所有分支的深度, 最后在根节点上取左右最短的一条 - if (left == 0) - return right + 1; - else if (right == 0) - return left + 1; - else - return Math.min(left, right) + 1; - } - - /** - * 使用层级遍历的方式: 循环+队列 一层一层遍历并记录层数, 当遇到第一个左右子节点为空的节点, 那这个节点就是最小深度的叶子节点, 返回的层数为最小层数(最小深度). - *

- * 时间复杂度为O(n), 空间复杂度为O(n) - * - * @param root - * @return - */ - private static int minDepthByLoop(TreeNode root) { - int level = 0; - if (root == null) - return level; - - Queue queue = new LinkedList<>(); - queue.offer(root); - - while (!queue.isEmpty()) { - level++; - int size = queue.size(); - for (int i = 0; i < size; i++) { - TreeNode node = queue.poll(); - // 如果节点的左右子树都为空, 那么这个节点为叶子节点, 也就是最小深度 - if (node.left == null && node.right == null) { - return level; - } - // 将左右节点按顺序放入队列中,用于下一层遍历 - if (node.left != null) { - queue.offer(node.left); - } - if (node.right != null) { - queue.offer(node.right); - } - } - } - return level; - } -} diff --git a/src/main/java/com/study/binarytree/NodesInALevel.java b/src/main/java/com/study/binarytree/NodesInALevel.java deleted file mode 100644 index bd04267..0000000 --- a/src/main/java/com/study/binarytree/NodesInALevel.java +++ /dev/null @@ -1,165 +0,0 @@ -package com.study.binarytree; - -import com.study.utils.TreeUtils; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; - -/** - * 求第k层有多少个节点, 根节点为第0层 - *

- * 我们知道: 一个满二叉树的第k层节点个数n为: n = 2 ^ k (根节点为第0层) - *

- * 而第k层的节点数与第(k - 1)层的节点数比例为 2 : 1 - *

- * https://www.cnblogs.com/hapjin/p/5505988.html - */ -public class NodesInALevel { - public static void main(String[] args) { - Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - - TreeNode root = TreeUtils.buildTree(arr); - - int k = 2; -// System.out.printf("第%d层节点数:%d\r\n", k, k_nodes(root, k)); -// System.out.println("分别为:"); - //List nodes = k_nodes2(root, k); - List nodes = k_nodes3(root, k); - for (TreeNode node : nodes) { - System.out.print(node.val + " "); - } - } - - /** - * 满二叉树 - * 1 1 - * 2 3 2 - * 4 5 6 7 4 - * 8 9 10 11 12 13 14 15 8 - *

- * 非满二叉树 - * 1 1 - * 2 3 2 - * 4 5 6 7 4 - * 8 9 10 11 12 5 - *

- * 正常情况下每多一层, 节点数都是上一层的两倍, 相当于 两个 上一层节点数相加 即 N(k) = N(k-1) + N(k-1) - *

- * - * @param root - * @param k - * @return - */ - private static int k_nodes(TreeNode root, int k) { - if (k < 0) { - return 0; - } - // 当root为空或者 递归到root为空, 说明到第k层或到k层之前就已经没有节点了 - if (root == null) { - //System.out.println(0); - return 0; - } - // 当k ==0 或者 递归到 k==0, 说明k层有节点 - if (k == 0) { - // 到达第k层节点数, 返回多少次1, 说明有多少个节点到达了第k层,(即第k层有多少个节点) - //System.out.println(1); - return 1; - } else { - // 递归一次 最多得到 1+1==2个节点, 递归两次 2+2 == 4个节点, 最终递归 k-1词 最多得到 2 ^ (k-1)个数节点 - // 如果中途有空节点, 那么只能算没有空节点的节点个数, 即递归到k==0时候的那些情况节点, 求1出现次数之和即是第k层节点数 - // 从最后一层 k==0的时候开始计算, 如果k==0的时候 返回 - -// int a = k_nodes(root.left, k - 1); -// int b = k_nodes(root.right, k - 1); -// return a + b; - // 先递归完左边子树的直到return返回值,然后往回走向根节点, 在走向根节点同时递归右子树 - // 每递归完一次右子树, 就将左右子树的结果进行相加. 并return给下一次递归相加 - return k_nodes(root.left, k - 1) + k_nodes(root.right, k - 1); - } - } - - /** - * 返回第k层的节点集合 - */ - private static List k_nodes2(TreeNode root, int k) { - List list = new ArrayList(); - if (k < 0) return list; - - //nodes(root, k, list); - nodes2(root, k, list); - return list; - } - - private static void nodes(TreeNode root, int k, List list) { - if (root == null) { - System.out.println("节点为空,递归往内中止"); - return; - } - - if (k == 0) { - // 将第k层节点加入到集合中 - list.add(root); - System.out.println("往集合中添加节点" + root.val); - } - - System.out.println(String.format("递归往内, 当前节点为%d, k=%d", root.val, k)); - - // 递归左(右)子树, 找出k=0节点 - nodes(root.left, k - 1, list); - System.out.println(String.format("递归左子树往外, 当前节点为%d, k=%d", root.val, k)); - - // 递归右(左)子树, 找出k=0节点 - nodes(root.right, k - 1, list); - System.out.println(String.format("递归右子树往外, 当前节点为%d, k=%d", root.val, k)); - } - - private static void nodes2(TreeNode root, int k, List list) { - if (root == null) - return; - - if (k == 0) { - list.add(root); - } - - // 无所谓左右递归顺序 - nodes2(root.right, k - 1, list); - nodes2(root.left, k - 1, list); - } - - /** - * 使用层级遍历的方式获取第k层节点数 - * - * @param root - * @param k - * @return - */ - private static List k_nodes3(TreeNode root, int k) { - List nodes = new ArrayList<>(); - - if (root == null) - return nodes; - - Queue queue = new LinkedList(); - queue.offer(root); - - while (!queue.isEmpty()) { - int size = queue.size(); - for (int i = 0; i < size; i++) { - TreeNode node = queue.poll(); - // 获取第k层节点 - if (k == 0) { - nodes.add(node); - } - if (node.left != null) - queue.offer(node.left); - if (node.right != null) - queue.offer(node.right); - } - if (k == 0) break; - k--; - } - return nodes; - } -} diff --git a/src/main/java/com/study/binarytree/PostorderTraversal.java b/src/main/java/com/study/binarytree/PostorderTraversal.java deleted file mode 100644 index c494102..0000000 --- a/src/main/java/com/study/binarytree/PostorderTraversal.java +++ /dev/null @@ -1,242 +0,0 @@ -package com.study.binarytree; - -import com.study.utils.TreeUtils; - -import java.util.ArrayList; -import java.util.List; -import java.util.Stack; - -/** - * 145. 二叉树的后序遍历 - *

- * 给定一个二叉树,返回它的 后序 遍历。 - *

- * 示例: - *

- * 输入: [1,null,2,3] - * 1 - * \ - * 2 - * / - * 3 - *

- * 输出: [3,2,1] - * 进阶: 递归算法很简单,你可以通过迭代算法完成吗? - *

- * https://leetcode-cn.com/problems/binary-tree-postorder-traversal - */ -public class PostorderTraversal { - public static void main(String[] args) { - Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - TreeNode root = TreeUtils.buildTree(arr); - - TreeUtils.show(root); - - //List list = postorderTraversal(root); - //List list = postorderTraversal2(root); - //List list = postorderTraversal3(root); - // List list = postorderTraversal4(root); -// for (Integer num : list) { -// System.out.print(num + " "); -// } - //Stack stack = postorderTraversal5(root); - Stack stack = postorderTraversal5_2(root); - while (!stack.isEmpty()) { - System.out.print(stack.pop() + " "); - } - } - - /** - * 使用递归方法进行后续遍历 - *

- * 后续遍历 左 - 右 - 根 - * - * @param root - * @return - */ - private static List postorderTraversal(TreeNode root) { - List list = new ArrayList(); - traversal(root, list); - return list; - } - - private static void traversal(TreeNode root, List list) { - if (root != null) { - traversal(root.left, list); - traversal(root.right, list); - list.add(root.val); - } - } - - /** - * 使用递归2 - * - * @param root - * @return - */ - private static List postorderTraversal2(TreeNode root) { - List list = new ArrayList(); - if (root != null) { - if (root.left != null) { - list.addAll(postorderTraversal2(root.left)); - } - - if (root.right != null) { - list.addAll(postorderTraversal2(root.right)); - } - - list.add(root.val); - } - return list; - } - - /** - * 使用迭代法后序遍历 - *

- * 从右边节点开始处理, 先右再左, 将节点值倒序插到集合中 - * - * @param root - * @return - */ - private static List postorderTraversal3(TreeNode root) { - List list = new ArrayList(); - Stack stack = new Stack(); - - if (root == null) { - return list; - } - - stack.push(root); - System.out.println(String.format("将根节点%d入栈", root.val)); - - while (!stack.isEmpty()) { - root = stack.pop(); - // 将节点值加到列表顶部 - list.add(0, root.val); - System.out.println(String.format("将节点%d加入到集合顶部", root.val)); - - //和前序比那里不一样, 先将左节点入栈 - if (root.left != null) { - stack.push(root.left); - System.out.println(String.format("将左节点%d入栈", root.left.val)); - } - - //再将右节点入栈 - if (root.right != null) { - stack.push(root.right); - System.out.println(String.format("将右节点%d入栈", root.right.val)); - } - } - return list; - } - - /** - * 通过迭代法+栈 实现后序遍历二叉树 - * 遍历出来的每个二叉树节点再使用栈来存储,最后遍历输出 - * 根节点最先进结果栈,最后出结果栈 - * - * @param root - * @return - */ - private static Stack postorderTraversal5(TreeNode root) { - Stack result = new Stack(); - Stack stack = new Stack(); - - if (root == null) { - return result; - } - - stack.push(root); - System.out.println(String.format("将根节点%d入栈", root.val)); - - while (!stack.isEmpty()) { - root = stack.pop(); - // 因为根节点是最后输出的,所以将根节点首先放入结果栈中(先进后出) - result.push(root.val); - System.out.println(String.format("将节点%d加入到栈中", root.val)); - - //和前序比那里不一样, 先将左节点入栈 - if (root.left != null) { - stack.push(root.left); - System.out.println(String.format("将左节点%d入栈", root.left.val)); - } - - //再将右节点入栈 - if (root.right != null) { - stack.push(root.right); - System.out.println(String.format("将右节点%d入栈", root.right.val)); - } - } - return result; - } - - - private static Stack postorderTraversal5_2(TreeNode root) { - Stack output = new Stack<>(); - Stack input = new Stack<>(); - - if (root == null) { - return output; - } - - input.push(root); - - while (!input.isEmpty()) { - TreeNode node = input.pop(); - output.push(node.val); - - if (node.left != null) - input.push(node.left); - - if (node.right != null) - input.push(node.right); - } - - return output; - } - - /** - * 迭代遍历 左 右 根 - *

- * 利用根节点和子节点的关系,来找出根节点并巧妙处理 - * - * @param root - * @return - */ - private static List postorderTraversal4(TreeNode root) { - List list = new ArrayList(); - Stack stack = new Stack(); - - if (root == null) { - return list; - } - - // 使用前置节点来找出其根节点 - TreeNode pre = null; - - stack.push(root); - while (!stack.isEmpty()) { - // 拿到栈顶上的元素, 不移除 - TreeNode cur = stack.peek(); - - // 如果当前节点没有子节点 或者 当前节点是前置节点的根节点 - if ((cur.left == null && cur.right == null || (pre != null && (pre == cur.left || pre == cur.right)))) { - list.add(cur.val); - // 记录前置节点 - pre = cur; - // 移除已处理的当前节点, 便于下一轮循环处理下一个节点 - stack.pop(); - } else { - // 先右子节点入栈, 再左子节点, 根据后入先出原则, 下一轮循环先处理左子节点 - if (cur.right != null) { - stack.push(cur.right); - } - if (cur.left != null) { - stack.push(cur.left); - } - } - } - - return list; - } -} diff --git a/src/main/java/com/study/binarytree/PreorderTraversal.java b/src/main/java/com/study/binarytree/PreorderTraversal.java deleted file mode 100644 index 4d61a53..0000000 --- a/src/main/java/com/study/binarytree/PreorderTraversal.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.study.binarytree; - -import com.study.utils.TreeUtils; - -import java.util.ArrayList; -import java.util.List; -import java.util.Stack; - -/** - * 144. 二叉树的前序遍历 深度优先遍历dfs - *

- * 给定一个二叉树,返回它的 前序 遍历。 前序遍历: 根 - 左 - 右 - *

- * 示例: - *

- * 输入: [1,null,2,3] - * 1 - * \ - * 2 - * / - * 3 - *

- * 输出: [1,2,3] - *

- * 进阶: 递归算法很简单,你可以通过迭代算法完成吗? - *

- * https://leetcode-cn.com/problems/binary-tree-preorder-traversal/ - */ -public class PreorderTraversal { - public static void main(String[] args) { - Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - TreeNode root = TreeUtils.buildTree(arr); - TreeUtils.show(root); - - //List list = preorderTraversal(root); - List list = preorderTraversalByLoop(root); - // List list = preorderTraversalByLoop2(root); - //List list = preorderTraversalByLoopWithList(root); - for (Integer num : list) { - System.out.print(num + " "); - } - } - - - /** - * 通过递归的方式遍历整个二叉树 时间复杂度O(n) - *

- * 前序遍历: 根 - 左 - 右 - * - * @param root - * @param list - */ - private static void traversal(TreeNode root, List list) { - if (root != null) { - list.add(root.val); - traversal(root.left, list); - traversal(root.right, list); - } - } - - - public static List preorderTraversal(TreeNode root) { - List list = new ArrayList(); - traversal(root, list); - return list; - } - - /** - * 使用迭代的方式 遍历二叉树 - *

- * 用到栈后入先出的原理, 将左子树放在右子树后面入栈, 确保左子树先弹出,优先处理左子树,之后再处理右子树 - *

- * 关于为什么循环中没专门放入根节点, 因为除了最顶层的根节点, 其他层级的根节点都是左右子树 - *

- * 时间复杂度O(n), 空间复杂度O(n) - * - * @param root - * @return - */ - public static List preorderTraversalByLoop(TreeNode root) { - List list = new ArrayList(); - Stack stack = new Stack(); - - if (root == null) { - return list; - } - - stack.push(root); - System.out.println(String.format("节点%d入栈", root.val)); - - while (!stack.isEmpty()) { - // 每次优先弹出左节点, - // 由于每次左子树都是最后放进去,而每次循环都只pop一次, 存在左子树优先pop左子树,没有再pop右节点 - TreeNode node = stack.pop(); - System.out.println(String.format("节点%d弹出", node.val)); - list.add(node.val); - - // 根据栈后进先出的原理, 把left节点放在right后面放入栈中 - if (node.right != null) { - stack.push(node.right); - System.out.println(String.format("右节点%d入栈", node.right.val)); - } - - if (node.left != null) { - stack.push(node.left); - System.out.println(String.format("左节点%d入栈", node.left.val)); - } - } - return list; - } - - public static List preorderTraversalByLoop2(TreeNode root) { - List nodes = new ArrayList<>(); - - if (root == null) - return nodes; - - Stack stack = new Stack<>(); - stack.push(root); //将节点放到栈顶 - - while (!stack.isEmpty()) { - - // 弹出栈顶的节点 - TreeNode node = stack.pop(); - nodes.add(node.val); - - if (node.right != null) { - stack.push(node.right); - } - - // 根据后进先出的原理,将左子树节点放在后面入栈 - if (node.left != null) { - stack.push(node.left); - } - } - return nodes; - } - - /** - * 使用循环 + 集合 实现前序遍历 - * 每次都将节点放到集合顶部, 并且从集合顶部取出节点 - * - * @param root - * @return - */ - public static List preorderTraversalByLoopWithList(TreeNode root) { - List nodes = new ArrayList<>(); - - if (root == null) - return nodes; - - List list = new ArrayList<>(); - list.add(0, root); //将节点放到集合顶部 - - while (!list.isEmpty()) { - - // 取出并删除集合顶部元素 - TreeNode node = list.remove(0); - nodes.add(node.val); - - if (node.right != null) { - // 将右节点放到集合顶部 - list.add(0, node.right); - } - - // 根据后进先出的原理,将左子树节点放在后面入栈 - if (node.left != null) { - // 将左节点放到集合顶部 - list.add(0, node.left); - } - } - return nodes; - } -} diff --git a/src/main/java/com/study/binarytree/TraverseTree.java b/src/main/java/com/study/binarytree/TraverseTree.java deleted file mode 100644 index 0b4daf8..0000000 --- a/src/main/java/com/study/binarytree/TraverseTree.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.study.binarytree; - -import com.study.utils.TreeUtils; - -import java.util.ArrayList; -import java.util.List; -import java.util.Stack; - -/** - * 遍历二叉树 二叉树有三种遍历模式 前序遍历 中序遍历 后续遍历 - *

- * 其中每种遍历 又有多种遍历方法 比如递归, 迭代 - */ -public class TraverseTree { - public static void main(String[] args) { - Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; - TreeNode root = TreeUtils.buildTree(arr); - - //preOrderReverse(root); // 0 1 3 7 4 2 5 6 - //preOrderReverse2(root); // 0 1 3 7 4 2 5 6 - //inOrderReverse(root); // 7 3 1 4 0 5 2 6 - inOrderReverse2(root); - //afterOrderReverse(root); //7 3 4 1 5 6 2 0 - } - - /** - * 前序遍历 根在前面 - *

- * 根 - 左 - 右 - *

- * 先输出根节点 - */ - private static void preOrderReverse(TreeNode node) { - if (node == null) { - return; - } - - System.out.printf("当前节点----------------%d\r\n", node.val); - - System.out.printf("当前节点%d的左节点为%s\r\n", node.val, node.left == null ? "空" : node.left.val); - preOrderReverse(node.left); - - System.out.printf("当前节点%d的右节点为%s\r\n", node.val, node.right == null ? "空" : node.right.val); - preOrderReverse(node.right); - } - - - /** - * 使用迭代法进行前序遍历 - * - * @param node - */ - private static void preOrderReverse2(TreeNode node) { - Stack stack = new Stack(); - - if (node == null) { - return; - } - - stack.push(node); - - while (!stack.isEmpty()) { - // 先处理根节点 - TreeNode tempNode = stack.pop(); - System.out.println(tempNode.val); - - //根据栈的后进先出原理, 将left节点放在right后面入栈 - if (tempNode.right != null) { - stack.push(tempNode.right); - } - - if (tempNode.left != null) { - stack.push(tempNode.left); - } - } - } - - /** - * 中序遍历 根在中间 - *

- * 左 - 根 - 右 - *

- * 根节点在左节点之后输出 - */ - private static void inOrderReverse(TreeNode node) { - if (node == null) { - return; - } - System.out.printf("当前节点%d的左节点为%s\r\n", node.val, node.left == null ? "空" : node.left.val); - inOrderReverse(node.left); - - System.out.printf("当前节点----------------%d\r\n", node.val); - - System.out.printf("当前节点%d的右节点为%s\r\n", node.val, node.right == null ? "空" : node.right.val); - inOrderReverse(node.right); - } - - /** - * 使用迭代法进行中序遍历 - * - * @param node - */ - private static void inOrderReverse2(TreeNode node) { - Stack stack = new Stack(); - - if (node == null) { - return; - } - - while (node != null || !stack.isEmpty()) { - - // 先将当前节点以及所有左节点入栈 - while (node != null) { - stack.push(node); - node = node.left; //左 - } - - if (!stack.isEmpty()) { - node = stack.pop(); //根 - System.out.println(node.val); - node = node.right; //右 - } - } - } - - /** - * 后续遍历 根在末尾 - *

- * 左 - 右 - 根 - *

- * 根节点在左右节点输出之后再输出 - */ - private static void afterOrderReverse(TreeNode node) { - if (node == null) { - return; - } - System.out.printf("当前节点%d的左节点为%s\r\n", node.val, node.left == null ? "空" : node.left.val); - afterOrderReverse(node.left); - - System.out.printf("当前节点%d的右节点为%s\r\n", node.val, node.right == null ? "空" : node.right.val); - afterOrderReverse(node.right); - - System.out.printf("当前节点----------------%d\r\n", node.val); - } -} diff --git a/src/main/java/com/study/binarytree/TreeNode.java b/src/main/java/com/study/binarytree/TreeNode.java deleted file mode 100644 index 6a70e87..0000000 --- a/src/main/java/com/study/binarytree/TreeNode.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.study.binarytree; - -public class TreeNode { - public TreeNode left; - public TreeNode right; - public Integer val; - - public TreeNode(Integer val) { - this.val = val; - } -} diff --git a/src/main/java/com/study/binarytree/TreeNode2.java b/src/main/java/com/study/binarytree/TreeNode2.java deleted file mode 100644 index 4321cb0..0000000 --- a/src/main/java/com/study/binarytree/TreeNode2.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.study.binarytree; - -public class TreeNode2 { - public TreeNode2 left; - public TreeNode2 right; - public String val; - - public TreeNode2(String val) { - this.val = val; - } -} diff --git a/src/main/java/com/study/binarytree/binarysearchtree/CRUD.java b/src/main/java/com/study/binarytree/binarysearchtree/CRUD.java deleted file mode 100644 index 2df6be8..0000000 --- a/src/main/java/com/study/binarytree/binarysearchtree/CRUD.java +++ /dev/null @@ -1,236 +0,0 @@ -package com.study.binarytree.binarysearchtree; - - -import com.study.binarytree.TreeNode; -import com.study.utils.TreeUtils; - -import java.util.List; - -/** - * 查找二叉搜索树的某个节点 - */ -public class CRUD { - - public static void main(String[] args) { - // 由于二叉搜索树也是二叉树, 所以既可以使用二叉搜索树的方法也可以使用二叉树的方法求最近公共祖先 - int[] arr = {5, 3, 8, 1, 4, 7, 9}; - List treeNodes = TreeUtils.buildTreeAndList(arr); - TreeNode root = treeNodes.get(0); - - TreeUtils.show(root); - - int target = 7; - //int target = 2; - //boolean result = searchByRecursion(root,target); - TreeNode result = searchByRecursion2(root, target); - //TreeNode result = searchByLoop(root, target); - //System.out.println(result != null ? result.val : "未找到"); - - TreeUtils.show(root); - //TreeNode node = insertByRecursion(root, 6); - //TreeNode node = insertByRecursion2(root, 6); - TreeNode node = insertByLoop(root, 6); - System.out.println(node.val); - TreeUtils.show(root); - } - - /** - * 后续遍历查找结果, 需要在递归的时候将参数传进去 - * - * @param root 根节点 - * @param target 目标值 - * @return 是否找到目标 - */ - private static boolean searchByRecursion(TreeNode root, int target) { - if (root == null) { - return false; // 未找到目标 - } - - if (root.val > target) { - // 将找到或者未找到目标不断回溯上去 - return searchByRecursion(root.left, target); - } - - if (root.val < target) { - // 将找到或者未找到目标不断回溯上去 - return searchByRecursion(root.right, target); - } - - // 找到目标 - return true; - } - - /** - * 查找目标节点 - * - * @param root 根节点 - * @param target 目标值 - * @return 目标节点 - */ - private static TreeNode searchByRecursion2(TreeNode root, int target) { - if (root == null) - return null; - - if (root.val > target) { - return searchByRecursion2(root.left, target); - } - - if (root.val < target) { - return searchByRecursion2(root.right, target); - } - - // 后续遍历 查找结果 - return root; - } - - /** - * 先序遍历查找目标节点 - * - * @param root - * @param target - * @return - */ - private static TreeNode searchByRecursion3(TreeNode root, int target) { - // 如果为空, 说明递归到了最底层 叶子节点也没找到 - if (root == null) { - System.out.println("没有找到目标节点"); - return null; - } - - // 如果找到了目标 - if (root.val == target) { - // 一旦找到, 返回结果 - System.out.println("找到了节点" + root.val); - System.out.println("开始回溯"); - return root; - } - - // 如果目标值小于根节点, 往左子树找 - if (root.val > target) { - // 找到目标节点后, 如果曾经从左边找过, 会从左边回溯,并返回结果 - System.out.println("从左子树节点找" + root.left.val); - TreeNode left = searchByRecursion2(root.left, target); - System.out.println("回溯到左子树节点" + root.left.val); - return left; - } else { - // 如果目标值大于根节点, 往右子树找 - System.out.println("从右子树节点找" + root.right.val); - TreeNode right = searchByRecursion2(root.right, target); - System.out.println("回溯到右子树节点" + root.right.val); - return right; - } - } - - /** - * 使用循环的方式,查找目标节点 - * - * @param root - * @param target - * @return - */ - private static TreeNode searchByLoop(TreeNode root, int target) { - while (root != null) { - // 找到目标节点 直接返回 - if (root.val == target) - return root; - - // 目标节点小于根节点 - if (root.val > target) { - // 往左找 - root = root.left; - } else { - // 大于目标节点往右找 - root = root.right; - } - } - // 循环结束的时候,还没找到 返回空 - return null; - } - - /** - * 往树中插入新节点 - *

- * 首先通过二分查找 找到该节点的位置, 如果节点存在, 直接返回, 如果不存在则创建并返回. - * - * @param root 根节点 - * @param value 要插入的节点 - * @return 根节点 - */ - private static TreeNode insertByRecursion(TreeNode root, int value) { - if (root == null) { - // 如果没找到, 创建该节点 - return new TreeNode(value); - } - - // 从左边找 - if (root.val > value) { - // 将返回的左节点添加到根节点左边, 如果创建了一个新的节点,将被接上 - root.left = insertByRecursion(root.left, value); - } - - if (root.val < value) { - // 从右边找 - // 将返回的右节点添加到根节点右边, 如果创建了一个新的节点,将被接上 - root.right = insertByRecursion(root.right, value); - } - - // 不断回溯到根节点 - return root; - } - - private static TreeNode insertByRecursion2(TreeNode root, int value) { - // 没找到就创建目标节点并返回 - if (root == null) { - return new TreeNode(value); - } - - if (root.val > value) { - // 从左边找 - root.left = insertByRecursion2(root.left, value); - } - if (root.val < value) { - // 从右边找 - root.right = insertByRecursion2(root.right, value); - } - - return root; - } - - /** - * 通过迭代的方式插入新节点 - * - * @param root 根 - * @param val 新节点值 - * @return 根节点 - */ - private static TreeNode insertByLoop(TreeNode root, int val) { - TreeNode result = root; - while (root != null) { - if (root.val > val) { - // 判断左节点是否为空, 为空就说明新节点不存在,则创建 - if (root.left == null) { - root.left = new TreeNode(val); - break; - } - // 往左边找 - root = root.left; - } - if (root.val < val) { - // 判断右节点是否为空, 为空就说明新节点不存在,则创建 - if (root.right == null) { - root.right = new TreeNode(val); - break; - } - // 往右边找 - root = root.right; - } - // 新节点已经存在,停止查找,无需新建 - if (root.val == val) - break; - } - - // 返回根节点 - return result; - } - -} diff --git a/src/main/java/com/study/binarytree/binarysearchtree/LowestCommonAncestor.java b/src/main/java/com/study/binarytree/binarysearchtree/LowestCommonAncestor.java deleted file mode 100644 index cae67d5..0000000 --- a/src/main/java/com/study/binarytree/binarysearchtree/LowestCommonAncestor.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.study.binarytree.binarysearchtree; - -import com.study.binarytree.TreeNode; -import com.study.utils.TreeUtils; - -import java.util.List; - -/** - * 二叉搜索树的最近公共祖先 lowest common ancestor - *

- * 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 - *

- * 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” - *

- * 例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5] - *

- * 示例 1: - *

- * 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 - * 输出: 6 - * 解释: 节点 2 和节点 8 的最近公共祖先是 6。 - * 示例 2: - *

- * 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4 - * 输出: 2 - * 解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。 - *

- *

- * 说明: - *

- * 所有节点的值都是唯一的。 - * p、q 为不同节点且均存在于给定的二叉搜索树中。 - *

- * https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/ - */ -public class LowestCommonAncestor { - public static void main(String[] args) { - - // 由于二叉搜索树也是二叉树, 所以既可以使用二叉搜索树的方法也可以使用二叉树的方法求最近公共祖先 - int[] arr = {5, 3, 7, 1, 4, 6, 8}; - List treeNodes = TreeUtils.buildTreeAndList(arr); - TreeNode root = treeNodes.get(0); - - TreeUtils.show(root); - - TreeNode p = treeNodes.get(5); - TreeNode q = treeNodes.get(6); - - //TreeNode lca = getlca(root, p, q); - TreeNode lca = getlca2(root, p, q); - if (lca != null) - System.out.println(lca.val); - } - - /** - * @param root - * @param p - * @param q - * @return - */ - private static TreeNode getlca(TreeNode root, TreeNode p, TreeNode q) { - - if (root == null) - return null; - if (p == root || q == root) - return root; - - // 递归根左子树, 查找p或q节点, 一旦找到立刻退出, 进行根右子树递归 - // 因为根左子树找到1个节点, 那么只要根右子树存在另一个节点, 那最近公共祖先就是根节点, 否则就是左子树找到的节点本身(另一个节点肯定在这个节点下面) - TreeNode left = getlca(root.left, p, q); - - // 递归右子树, 查看右边是否也存在目标节点 - TreeNode right = getlca(root.right, p, q); - - // 如果左右都存在, 直接返回当前节点 - if (left != null && right != null) - return root; - - // 如果左右都没找到, 说明最近公共不存在 - if (left == null && right == null) - return null; - - //如果左边不存在,而右边存在返回右边, 如果右边不存在,左边存在,返回左边 - return left == null ? right : left; - } - - /** - * 使用二叉搜索树 左子树所有节点 < 父节点 < 右子树所有节点 的原理 - * - * 将节点和根节点比较, 从左子树找 或者 右子树找, 不需要全树遍历, 类似二叉查找 - * - * 时间复杂度为O(logN) - * - * @param root - * @param p - * @param q - * @return - */ - private static TreeNode getlca2(TreeNode root, TreeNode p, TreeNode q) { - // 如果 p和q的值 都小于 root根节点, 说明两个节点都在左子树, 直接从树的左边去找 - if (p.val < root.val && root.val > q.val) - return getlca2(root.left, p, q); - // 如果 p和q的值都大于 root, 说明量两个节点都在右子树, 往树的右边去找 - if (p.val > root.val && q.val > root.val) - return getlca2(root.right, p, q); - // 如果 p.val < root.val < q.val 或 p.val > root.val > q.val 则直接返回root节点 - return root; - } -} diff --git a/src/main/java/com/study/binarytree/binarysearchtree/ValidateBinarySearchTree.java b/src/main/java/com/study/binarytree/binarysearchtree/ValidateBinarySearchTree.java deleted file mode 100644 index 9de90c9..0000000 --- a/src/main/java/com/study/binarytree/binarysearchtree/ValidateBinarySearchTree.java +++ /dev/null @@ -1,237 +0,0 @@ -package com.study.binarytree.binarysearchtree; - -import com.study.binarytree.TreeNode; -import com.study.utils.TreeUtils; - -import java.util.ArrayList; -import java.util.List; - -/** - * 验证二叉搜索树 又名 二叉排序树/二叉平衡(查找)树 - *

- * BinarySearchTree BST - * - *

- * 给定一个二叉树,判断其是否是一个有效的二叉搜索树。 - *

- * 假设一个二叉搜索树具有如下特征: - *

- * 节点的左子树只包含小于当前节点的数。 - * 节点的右子树只包含大于当前节点的数。 - * 所有左子树和右子树自身必须也是二叉搜索树。 - * 示例 1: - *

- * 输入: - * 2 - * / \ - * 1 3 - * 输出: true - * 示例 2: - *

- * 输入: - * 5 - * / \ - * 1 4 - * / \ - * 3 6 - * 输出: false - * 解释: 输入为: [5,1,4,null,null,3,6]。 - * 根节点的值为 5 ,但是其右子节点值为 4 。 - *

- * https://leetcode-cn.com/problems/validate-binary-search-tree/ - */ -public class ValidateBinarySearchTree { - /** - * 节点的左子树只包含小于当前节点的数。 - * 节点的右子树只包含大于当前节点的数。 - * 所有左子树和右子树自身必须也是二叉搜索树。 - * - * @param args - */ - public static void main(String[] args) { - //Integer[] arr = {5, 3, 7, 1, 4, 6, 8}; - //Integer[] arr = {1, 1}; - - Integer[] arr = {5, 1, 6, null, null, 3, 7}; - - //Integer[] arr = {5, 1, 9, null, null, 7, 10, null, null, null, null, 8, 3}; - - //Integer[] arr = {3, 9, 20, null, null, 15, 7}; - - TreeNode root = TreeUtils.buildTree(arr); - System.out.println("中序遍历-----"); - List nodes = new ArrayList(); - inorder(root, nodes); // 验证树是否正确,从小到大输出 - - for (TreeNode node : nodes) { - System.out.print(node.val + " "); - } - System.out.println("\r\n中序遍历-----"); - - System.out.println("打印出树的结构-----"); - // 将刚刚创建的树打印出来 - TreeUtils.show(root); - System.out.println("打印出树的结构-----"); - - // 中序遍历获取所有节点 判断是否为有序节点 - //System.out.println(isValidBST_InOrder(root)); - //System.out.println(isValidBST_InOrder_2(root)); - - // 递归的方式判断是否所有子树 都满足 左子树<节点<右子树 - System.out.println(isValidBST(root)); - //System.out.println(isValidBST_2(root)); - - //System.out.println(isValidBST2(root)); - } - - - /** - * 判断该tree是否为一个有效的二叉搜索树 - *

- * 先使用中序遍历, 将每个节点放到一个arraylist集合中 - *

- * 然后遍历集合,对比前后两个元素, 看看是不是后面的元素都比前面元素大,如果是则说明集合是从小到大排列 - *

- * 同理说明二叉搜索树里的节点也是按左 根 右 从小到大排列的 - * - * @param root - * @return - */ - private static boolean isValidBST_InOrder(TreeNode root) { - List nodes = new ArrayList(); - inorder(root, nodes); // 验证树是否正确,从小到大输出 - - for (int i = 0; i < nodes.size() - 1; i++) { - //如果前一个元素大于后一个元素, 则说明不是有效二叉搜索树 - if (nodes.get(i).val >= nodes.get(i + 1).val) { - return false; - } - } - return true; - } - - private static boolean isValidBST_InOrder_2(TreeNode root) { - List nodes = new ArrayList<>(); - // 中序遍历后, 返回的节点为从小到大有序的 - inorder_2(root, nodes); - - for (int i = 0; i < nodes.size() - 1; i++) { - // 如果后面的节点大于前面的节点 说明它不是二叉搜索树 - if (nodes.get(i).val >= nodes.get(i + 1).val) { - return false; - } - } - return true; - } - - /** - * 验证是否为二叉搜索树, 需要判断左边所有与节点值是否小于根节点, 右边所有节点值 大于 根节点. - * 以及每棵子树 都是 左子树 < 节点 < 右子树 - *

- * 使用深度优先搜索: 需要把最小值min和最大值max传入, 让左子树所有节点与根节点值min作比较, 让右子树所有节点与根节点值max作比较. - * 对于根节点左边所有子树, 最大值为根节点max; 对于根节点右边所有子树,最小值为根节点min; - * - *

- * 节点的左子树只包含小于当前节点的数。 - * 节点的右子树只包含大于当前节点的数。 - * 所有左子树和右子树自身必须也是二叉搜索树。 - * - * 时间复杂度 : O(N)。每个结点访问一次。 - * 空间复杂度 : O(N)。我们跟进了整棵树。 - * - * @param root - * @return - */ - private static boolean isValidBST(TreeNode root){ - return helper(root,null,null); - } - - private static boolean helper(TreeNode root, Integer min, Integer max) { - if (root == null) //如果root为空 说明当前节点已经递归到最底层了 或者树本身就是空树 - return true; - //如果root小于最小值, 不是二叉搜索树 - if (min != null && root.val < min) { - System.out.println(String.format("不满足的节点: node: %d, min: %d", root.val, min)); - return false; - } - - //如果root大于最大值, 也不是二叉搜索树 - if (max != null && root.val > max) { - System.out.println(String.format("不满足的节点: node: %d, max: %d", root.val, max)); - return false; - } - - //System.out.print(root.val + " "); - - // 是否左子树的所有节点肯定都比根节点小 右子树的所有节点都比根大 - boolean left = helper(root.left, min, root.val); - boolean right = helper(root.right, root.val, max); - - return left && right; //如果左边和右边都满足条件, 则说明该数为二叉搜索树 - //return isValidBST(root.left, min, root.val) && isValidBST(root.right, root.val, max); - } - - private static boolean isValidBST_2(TreeNode root){ - return helper2(root,null,null); - } - - private static boolean helper2(TreeNode root, Integer min, Integer max) { - if (root == null) - return true; - if (min != null && root.val < min) //跟节点不能小于最小值 - return false; - if (max != null && root.val > max) //跟节点也不能大于最大值 - return false; - - return helper2(root.left, min, root.val) && helper2(root.right, root.val, max); - } - - /* 不对的做法, 这只判断了每个子树 是否满足 左子节点 < 节点 < 右子节点, 没有需要判断 左边子树所有节点 < 根节点 < 右边子树所有节点 */ -// /** -// * 验证是否为二叉搜索树, 需要判断左边所有与节点值是否小于根节点, 右边所有节点值 大于 根节点. -// * 不传入根节点值, 但是需要判断值左右节点值是否为空 -// * -// * @param root -// * @return -// */ -// private static boolean isValidBST2(TreeNode root) { -// if (root == null) -// return true; -// -// if (root.left != null && root.left.val != null && root.val < root.left.val) { -// System.out.println(String.format("不满足的左节点: node: %d, root: %d", root.left.val, root.val)); -// return false; -// } -// -// if (root.right != null && root.right.val != null && root.val > root.right.val) { -// System.out.println(String.format("不满足右的节点: node: %d, root: %d", root.right.val, root.val)); -// return false; -// } -// -// return isValidBST2(root.left) && isValidBST2(root.right); -// } - - /** - * 使用中序遍历 获取二叉树每个节点 - *

- * 左 根 右 - * - * @param root - */ - private static void inorder(TreeNode root, List nodes) { - if (root != null) { - inorder(root.left, nodes); - nodes.add(root); - inorder(root.right, nodes); - } - } - - - private static void inorder_2(TreeNode root, List nodes) { - if (root != null) { - inorder(root.left, nodes); - nodes.add(root); - inorder(root.right, nodes); - } - } -} diff --git a/src/main/java/com/study/ntree/MaximumDepthOfNAryTree.java b/src/main/java/com/study/tree/ntree/MaximumDepthOfNAryTree.java similarity index 97% rename from src/main/java/com/study/ntree/MaximumDepthOfNAryTree.java rename to src/main/java/com/study/tree/ntree/MaximumDepthOfNAryTree.java index c140a36..373140b 100644 --- a/src/main/java/com/study/ntree/MaximumDepthOfNAryTree.java +++ b/src/main/java/com/study/tree/ntree/MaximumDepthOfNAryTree.java @@ -1,4 +1,4 @@ -package com.study.ntree; +package com.study.tree.ntree; import java.util.ArrayList; import java.util.Collections; diff --git a/src/main/java/com/study/ntree/NTreeNode.java b/src/main/java/com/study/tree/ntree/NTreeNode.java similarity index 92% rename from src/main/java/com/study/ntree/NTreeNode.java rename to src/main/java/com/study/tree/ntree/NTreeNode.java index 133b47a..80be5ff 100644 --- a/src/main/java/com/study/ntree/NTreeNode.java +++ b/src/main/java/com/study/tree/ntree/NTreeNode.java @@ -1,4 +1,4 @@ -package com.study.ntree; +package com.study.tree.ntree; import java.util.List; diff --git a/src/main/java/com/study/trietree/Trie.java b/src/main/java/com/study/trietree/Trie.java deleted file mode 100644 index 6c8d52b..0000000 --- a/src/main/java/com/study/trietree/Trie.java +++ /dev/null @@ -1,231 +0,0 @@ -package com.study.trietree; - -/** - * 字典树 也叫 trie树 - *

- * 基本性质 - * 1.根节点不包含字符,除根节点外的每一个子节点都包含一个字符 - * 2.从根节点到某一节点。路径上经过的字符连接起来,就是该节点对应的字符串 - * 3.每个节点的所有子节点包含的字符都不相同 - *

- * 应用场景 - * 典型应用是用于统计,排序和保存大量的字符串(不仅限于字符串),经常被搜索引擎系统用于文本词频统计。 - *

- * 优点 - * 利用字符串的公共前缀来减少查询时间,最大限度的减少无谓的字符串比较,查询效率比哈希树高。 - *

- * 遍历算法: bfs深度优先遍历 先根再子节点 - * - *

- * https://www.cnblogs.com/xujian2014/p/5614724.html - *

- * https://leetcode-cn.com/problems/implement-trie-prefix-tree/ - */ -public class Trie { - public static void main(String[] args) { - TrieNode root = new TrieNode(); - String[] strs = {"banana", "band", "bee", "absolute", "acm",}; - String[] prefix = {"ba", "b", "band", "abc",}; -// String[] strs = {"北京大学", "北京科技大学", "南京北京路", "南京大学", "南京金陵十二拆",}; -// String[] prefix = {"北", "北京", "北京大学", "南京", "上海浦东新区",}; - for (String str : strs) { - insert(str, root); - } - - System.out.println("------打印字典树中所有单词--------"); - printAllWords(root, "#"); - System.out.println("--------------------------------"); - - // 是否包含这个单词 - System.out.println("------判断是否有ba这个单词--------"); - System.out.println(containsWord("ba", root)); - //System.out.println(containsWord("北京", root)); - System.out.println("--------------------------------"); - - // 打印包含该前缀的单词 - System.out.println("-------打印所有包含ba前缀的单词----"); - System.out.println(hasPrefix("ba", root)); - System.out.println("--------------------------------"); - - //preTraverse(root); - preTraverse2(root); - System.out.println(); - for (String pre : prefix) { - int num = countPrefix(pre, root); - System.out.println(pre + " 数量:" + num); - } - } - - /** - * 在字典树中插入一个单词 - * - * @param str - */ - public static void insert(String str, TrieNode root) { - if (str == null || str.length() == 0) { - return; - } - TrieNode node = root; - char[] letters = str.toCharArray();//将目标单词转换为char数组 - for (int i = 0, len = str.length(); i < len; i++) { - //计算每个char的位置(哈希值), 这样在当前层级查找该元素或节点的的时间复杂度为O(1) - int pos = letters[i] - 'a'; - if (node.son[pos] == null) { //如果当前节点的儿子节点中没有该字符,则构建一个TrieNode并复值该字符 - node.son[pos] = new TrieNode(); - node.son[pos].val = letters[i]; - } else { //如果已经存在,则将由根至该儿子节点组成的字符串模式出现的次数+1 - node.son[pos].num++; - } - node = node.son[pos]; - } - node.isEnd = true; - } - - /** - * 计算拥有该前缀的单词 - * - * @param prefix - * @return - */ - public static int countPrefix(String prefix, TrieNode root) { - if (prefix == null || prefix.length() == 0) { - return -1; - } - TrieNode node = root; - char[] letters = prefix.toCharArray(); - for (int i = 0, len = prefix.length(); i < len; i++) { - int pos = letters[i] - 'a'; - if (node.son[pos] == null) { - return 0; - } else { - node = node.son[pos]; - } - } - return node.num; - } - - /** - * 找出并打印出拥有指定前缀的所有单词 - * - * @param prefix - * @return - */ - public static String hasPrefix(String prefix, TrieNode root) { - if (prefix == null || prefix.length() == 0) { - return null; - } - TrieNode node = root; - char[] letters = prefix.toCharArray(); - for (int i = 0, len = prefix.length(); i < len; i++) { - int pos = letters[i] - 'a'; - if (node.son[pos] == null) { - return null; - } else { - node = node.son[pos]; - } - } - preTraverse(node, prefix); - return null; - } - - /** - * 打印经过此节点的所有单词. - * - * @param root - * @param prefix - */ - public static void preTraverse(TrieNode root, String prefix) { - if (!root.isEnd) { - for (TrieNode child : root.son) { - if (child != null) { - preTraverse(child, prefix + child.val); - } - } - return; - } - System.out.println(prefix); - } - - /** - * 在字典树中查找一个完全匹配的单词 - * - * @param str - * @return - */ - public static boolean containsWord(String str, TrieNode root) { - if (str == null || str.length() == 0) { - return false; - } - TrieNode node = root; - char[] letters = str.toCharArray(); - for (int i = 0, len = str.length(); i < len; i++) { - int pos = letters[i] - 'a'; - if (node.son[pos] != null) { - node = node.son[pos]; - } else { - return false; - } - } - //走到这一步,表明可能完全匹配,可能部分匹配,如果最后一个字符节点为末端节点,则是完全匹配,否则是部分匹配 - return node.isEnd; - } - - /** - * 打印字典树里面所有字符char - *

- * 前序遍历字典树. bfs深度优先遍历 - * - *

- * 根 - 所有子节点 - * - * @param root - */ - public static void preTraverse(TrieNode root) { - if (root == null) { - return; - } - System.out.print(root.val + ":" + root.num + " "); - for (TrieNode child : root.son) { - preTraverse(child); - } - } - - /** - * 打印字典树里面所有字符char - * - * @param root - */ - public static void preTraverse2(TrieNode root) { - if (root == null) { - return; - } - for (TrieNode child : root.son) { - if (child != null) { - System.out.print(child.val + ":" + child.num + " "); - if (child.isEnd) - System.out.println(); - } - preTraverse2(child); - } - } - - /** - * 打印字典树里所有的单词 - * 将递归走的每个char进行拼接,直到单词尾部,然后输出这个单词 - * - * @param root 根节点 - * @param prefix 前缀 - */ - private static void printAllWords(TrieNode root, String prefix) { - if (root != null && root.isEnd) { - System.out.println(prefix); - return; - } - if (root == null) - return; - for (TrieNode child : root.son) { - if (child != null) - printAllWords(child, prefix + child.val); - } - } -} \ No newline at end of file diff --git a/src/main/java/com/study/trietree/TrieNode.java b/src/main/java/com/study/trietree/TrieNode.java deleted file mode 100644 index 391e459..0000000 --- a/src/main/java/com/study/trietree/TrieNode.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.study.trietree; - -/** - * 字典树节点 - */ -public class TrieNode -{ - /** - * 中文 - */ - //private int SIZE = 65536; - - /** - * 英文 一共26个字母 - */ - private int SIZE = 256; - - public int num;// 有多少单词通过这个节点,即由根至该节点组成的字符串模式出现的次数 - public TrieNode[] son = new TrieNode[SIZE];// 所有的儿子节点 - public boolean isEnd;// 是不是最后一个节点 - public char val = '#';// 节点的值 - - public TrieNode() { - num = 1; - isEnd = false; - } -} \ No newline at end of file From e6830edd2e39492fb12ab2fc45b953b8ae312f9a Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 2 Mar 2020 00:30:26 +0800 Subject: [PATCH 68/74] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=A0=91=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=9A=84=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/tree/binarytree/ArrayToTree.java | 125 +++++++++ .../{search => tree/binarytree}/BFS.java | 2 +- .../tree/binarytree/BalancedBinaryTree.java | 144 +++++++++++ .../{search => tree/binarytree}/DFS.java | 2 +- .../tree/binarytree/InorderTraversal.java | 159 ++++++++++++ .../com/study/tree/binarytree/InvertTree.java | 104 ++++++++ .../study/tree/binarytree/LevelTraversal.java | 184 +++++++++++++ .../tree/binarytree/LevelTraversal2.java | 82 ++++++ .../tree/binarytree/LowestCommonAncestor.java | 123 +++++++++ .../study/tree/binarytree/MaximumDepth.java | 132 ++++++++++ .../study/tree/binarytree/MinimumDepth.java | 145 +++++++++++ .../study/tree/binarytree/NodesInALevel.java | 165 ++++++++++++ .../tree/binarytree/PostorderTraversal.java | 242 ++++++++++++++++++ .../tree/binarytree/PreorderTraversal.java | 174 +++++++++++++ .../tree/binarytree/PreorderTraversal2.java | 59 +++++ .../study/tree/binarytree/TraverseTree.java | 145 +++++++++++ .../com/study/tree/binarytree/TreeNode.java | 11 + .../com/study/tree/binarytree/TreeNode2.java | 11 + .../binarytree/binarysearchtree/CRUD.java | 235 +++++++++++++++++ .../LowestCommonAncestor.java | 109 ++++++++ .../ValidateBinarySearchTree.java | 237 +++++++++++++++++ .../java/com/study/tree/trietree/Trie.java | 231 +++++++++++++++++ .../com/study/tree/trietree/TrieNode.java | 27 ++ 23 files changed, 2846 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/study/tree/binarytree/ArrayToTree.java rename src/main/java/com/study/{search => tree/binarytree}/BFS.java (99%) create mode 100644 src/main/java/com/study/tree/binarytree/BalancedBinaryTree.java rename src/main/java/com/study/{search => tree/binarytree}/DFS.java (98%) create mode 100644 src/main/java/com/study/tree/binarytree/InorderTraversal.java create mode 100644 src/main/java/com/study/tree/binarytree/InvertTree.java create mode 100644 src/main/java/com/study/tree/binarytree/LevelTraversal.java create mode 100644 src/main/java/com/study/tree/binarytree/LevelTraversal2.java create mode 100644 src/main/java/com/study/tree/binarytree/LowestCommonAncestor.java create mode 100644 src/main/java/com/study/tree/binarytree/MaximumDepth.java create mode 100644 src/main/java/com/study/tree/binarytree/MinimumDepth.java create mode 100644 src/main/java/com/study/tree/binarytree/NodesInALevel.java create mode 100644 src/main/java/com/study/tree/binarytree/PostorderTraversal.java create mode 100644 src/main/java/com/study/tree/binarytree/PreorderTraversal.java create mode 100644 src/main/java/com/study/tree/binarytree/PreorderTraversal2.java create mode 100644 src/main/java/com/study/tree/binarytree/TraverseTree.java create mode 100644 src/main/java/com/study/tree/binarytree/TreeNode.java create mode 100644 src/main/java/com/study/tree/binarytree/TreeNode2.java create mode 100644 src/main/java/com/study/tree/binarytree/binarysearchtree/CRUD.java create mode 100644 src/main/java/com/study/tree/binarytree/binarysearchtree/LowestCommonAncestor.java create mode 100644 src/main/java/com/study/tree/binarytree/binarysearchtree/ValidateBinarySearchTree.java create mode 100644 src/main/java/com/study/tree/trietree/Trie.java create mode 100644 src/main/java/com/study/tree/trietree/TrieNode.java diff --git a/src/main/java/com/study/tree/binarytree/ArrayToTree.java b/src/main/java/com/study/tree/binarytree/ArrayToTree.java new file mode 100644 index 0000000..1a84ca9 --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/ArrayToTree.java @@ -0,0 +1,125 @@ +package com.study.tree.binarytree; + + +import java.util.*; + +/** + * 有一个数组, 存着二叉树所有不为空的叶子节点, 通过该数组构建整个二叉树 + *

+ * array = [-9,8,3,-4,1] + *

+ * int v1 = hash(-9,8); + * v2 = hash(3,-4) + * v3 = hash(v1,v2) + * v4 = hash(1,0) + * v5 = hash(v4,0) + *

+ * root + * v3 v5 + * v1 v2 v4 0 + * -9 8 3 -4 1 0 0 0 + */ +public class ArrayToTree { + + public static void main(String[] args) { + int[] arr = {-9, 8, 3, -4, 1}; + + buildTree(arr, 4); + } + + /** + * 构建二叉树 + */ + private static void buildTree(int[] arr, int level) { + // 记算叶子节点总结点数 + int fullNodes = 1; + int i = 1; + while (i < level) { + fullNodes *= 2; + i++; + } + + List> allNodes = new ArrayList<>(); + + List knifeNodes = new ArrayList<>(); + // 将叶子节点放入集合中 + for (int a = 0; a < arr.length; a++) { + knifeNodes.add(new TreeNode(arr[a])); + } + + i = arr.length; + // 将叶子节点补全 + while (i < fullNodes) { + knifeNodes.add(new TreeNode(0)); + i++; + } + + allNodes.add(knifeNodes); + + // 构建叶子节点以上所有的层节点, 并把他们加入到集合中 + int currentLevel = 0; + while (currentLevel < level - 1) { + List parentNodes = new ArrayList<>(); + // 构造父节点 + for (i = 0; i < allNodes.get(currentLevel).size(); i += 2) { + parentNodes.add(getParentNode(allNodes.get(currentLevel).get(i), allNodes.get(currentLevel).get(i + 1))); + } + allNodes.add(parentNodes); + + currentLevel++; + } + + TreeNode root = allNodes.get(currentLevel).get(0); + System.out.println(root.val); + + // 通过根节点遍历树 + List> lists = levelOrder(root); + for (List nodesInALevel : lists) { + System.out.println(); + for (Integer node : nodesInALevel) { + System.out.print(node + " "); + } + } + } + + private static List> levelOrder(TreeNode root) { + // 创建一个2维的节点集合, 第1维度为层集合,第2维度为每层的节点集合 + List> levels = new ArrayList>(); + //如果根节点为空,直接返回空列表 + if (root == null) return levels; + + // 创建一个当前层的队列 + Queue queue = new LinkedList(); + queue.add(root); + + while (!queue.isEmpty()) { + // 往节点结合中添加当前层级的空节点集合 + List currentLevelNodes = new ArrayList<>(); + // 获取当前层次的节点个数 + int level_length = queue.size(); + // 遍历当前层的节点个数 + for (int i = 0; i < level_length; i++) { + TreeNode node = queue.remove();//取出并移除队列的第一个节点 根据先进先出原则, 取出的顺序为 根 左 右(根据添加时候的顺序一致) + + // 往节点集合中添加当前层级的节点 + currentLevelNodes.add(node.val); + + // 往当前层队列中添加当前层的子节点,用于下一次迭代处理 + if (node.left != null) + queue.add(node.left); + if (node.right != null) + queue.add(node.right); + } + levels.add(currentLevelNodes); + } + return levels; + } + + private static TreeNode getParentNode(TreeNode left, TreeNode right) { + int parentValue = left.val + right.val; + TreeNode parentNode = new TreeNode(parentValue); + parentNode.left = left; + parentNode.right = right; + return parentNode; + } +} diff --git a/src/main/java/com/study/search/BFS.java b/src/main/java/com/study/tree/binarytree/BFS.java similarity index 99% rename from src/main/java/com/study/search/BFS.java rename to src/main/java/com/study/tree/binarytree/BFS.java index 1c56c7c..d0e0589 100644 --- a/src/main/java/com/study/search/BFS.java +++ b/src/main/java/com/study/tree/binarytree/BFS.java @@ -1,4 +1,4 @@ -package com.study.search; +package com.study.tree.binarytree; import com.study.binarytree.TreeNode; import com.study.utils.TreeUtils; diff --git a/src/main/java/com/study/tree/binarytree/BalancedBinaryTree.java b/src/main/java/com/study/tree/binarytree/BalancedBinaryTree.java new file mode 100644 index 0000000..65bb57b --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/BalancedBinaryTree.java @@ -0,0 +1,144 @@ +package com.study.tree.binarytree; + +import com.study.utils.TreeUtils; + +/** + * 验证平衡二叉树 + * + * 给定一个二叉树,判断它是否是高度平衡的二叉树。 + *

+ * 本题中,一棵高度平衡二叉树定义为: + *

+ * 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。 + *

+ * 示例 1: + *

+ * 给定二叉树 [3,9,20,null,null,15,7] + *

+ * 3 + * / \ + * 9 20 + * / \ + * 15 7 + * 返回 true 。 + *

+ * 示例 2: + *

+ * 给定二叉树 [1,2,2,3,3,null,null,4,4] + *

+ * 1 + * / \ + * 2 2 + * / \ + * 3 3 + * / \ + * 4 4 + * 返回 false 。 + *

+ * https://leetcode-cn.com/problems/balanced-binary-tree/ + */ +public class BalancedBinaryTree { + + public static void main(String[] args) { + //Integer[] nodes = {3, 9, 20, null, null, 15, 7}; + Integer[] nodes = {1, 2, 2, 3, 3, null, null, 4, 4}; + TreeNode root = TreeUtils.buildTree(nodes); + TreeUtils.show(root); + + //System.out.println(isBalanced(root)); + System.out.println(isBalanced2(root)); + } + + /** + * 使用前序遍历(根左右),先处理根节点获取高度, 再遍历左右子树是否为平衡二叉树. 从顶至底(暴力法) + * 通过比较左右子树最大高度差是否大于1 来判断以此节点为根节点下是否是二叉平衡树 + *

+ * 从顶至底DFS,以每个节点为根节点,递归判断是否是平衡二叉树: + * 若所有根节点都满足平衡二叉树性质,则返回 True ; + * 若其中任何一个节点作为根节点时,不满足平衡二叉树性质,则返回False。 + *

+ * 最差时间复杂度为O(N^2) + *

+ * 作者:jyd + * 链接:https://leetcode-cn.com/problems/balanced-binary-tree/solution/balanced-binary-tree-di-gui-fang-fa-by-jin40789108/ + * + * @param root + * @return + */ + public static boolean isBalanced(TreeNode root) { + // 根节点为空, 说明是空树,肯定是平衡的 + if (root == null) + return true; + + // 先处理根节点, 获取高度 + // 获取每个节点的左右子树的高度 + int left = getHeight(root.left); + int right = getHeight(root.right); + + // 判断当前root节点为根的树是否为平衡二叉树 + if (Math.abs(left - right) > 1) + return false; + + // 再进行左右子树遍历 + // 如果当前树为平衡树, 那么继续判断递归当前树的左右子树是否为平衡二叉树 + return isBalanced(root.left) && isBalanced(root.right); + } + + /** + * 获取一颗树的最大高度 + * + * @param root + * @return + */ + private static int getHeight(TreeNode root) { + if (root == null) + return 0; + + int left = getHeight(root.left); + + int right = getHeight(root.right); + + return Math.max(left, right) + 1; + } + + /** + * 后续遍历(左右根), 先获取左右子树的高度, 再作判断是否为平衡二叉树 + * 从底部到顶部, 获取每个节点为root根时候的子树最大高度差是否大于1(不平衡), 如果是返回-1; 否则返回当前子树的最大高度 + * + * 时间复杂度为O(n) + * + * https://leetcode-cn.com/problems/balanced-binary-tree/solution/balanced-binary-tree-di-gui-fang-fa-by-jin40789108/ + * + * @return + */ + private static boolean isBalanced2(TreeNode root) { + return depth(root) != -1; + } + + /** + * 后续遍历 + * @param root + * @return + */ + private static int depth(TreeNode root) { + if (root == null) + return 0; + + // 左子树出现不满足条件的 + int left = depth(root.left); + if (left == -1) + return -1; + + // 右子树出现不满足条件的 + int right = depth(root.right); + if (right == -1) + return -1; + + // 检查是否满足条件 + if (Math.abs(left - right) > 1) + return -1; + + // 返回当前左右子树的最大高都 + return Math.max(left, right) + 1; + } +} diff --git a/src/main/java/com/study/search/DFS.java b/src/main/java/com/study/tree/binarytree/DFS.java similarity index 98% rename from src/main/java/com/study/search/DFS.java rename to src/main/java/com/study/tree/binarytree/DFS.java index ea7fe67..36452e7 100644 --- a/src/main/java/com/study/search/DFS.java +++ b/src/main/java/com/study/tree/binarytree/DFS.java @@ -1,4 +1,4 @@ -package com.study.search; +package com.study.tree.binarytree; import com.study.binarytree.TreeNode; import com.study.utils.TreeUtils; diff --git a/src/main/java/com/study/tree/binarytree/InorderTraversal.java b/src/main/java/com/study/tree/binarytree/InorderTraversal.java new file mode 100644 index 0000000..bc01fac --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/InorderTraversal.java @@ -0,0 +1,159 @@ +package com.study.tree.binarytree; + +import com.study.utils.TreeUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * 94. 二叉树的中序遍历 + *

+ * 给定一个二叉树,返回它的中序 遍历。 + *

+ * 示例: + *

+ * 输入: [1,null,2,3] + * 1 + * \ + * 2 + * / + * 3 + *

+ * 输出: [1,3,2] + * 进阶: 递归算法很简单,你可以通过迭代算法完成吗? + *

+ * https://leetcode-cn.com/problems/binary-tree-inorder-traversal/ + */ +public class InorderTraversal { + + public static void main(String[] args) { + Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + + TreeNode root = TreeUtils.buildTree(arr); + TreeUtils.show(root); + + //List list = inorderTraversal(root); + //List list = inorderTraversal2(root); + //List list = inorderTraversal3(root); + List list = inorderTraversal4(root); + for (Integer num : list) { + System.out.print(num + " "); + } + } + + /** + * 递归遍历 左 - 根 - 右 + * + * @param root + * @return + */ + private static List inorderTraversal(TreeNode root) { + List list = new ArrayList(); + traversal(root, list); + return list; + } + + private static void traversal(TreeNode root, List list) { + if (root != null) { + //递归左子树 + traversal(root.left, list); + list.add(root.val); + //递归右子树 + traversal(root.right, list); + } + } + + + /** + * 递归遍历2 左 - 根 - 右 + * + * @param root + * @return + */ + private static List inorderTraversal2(TreeNode root) { + List list = new ArrayList(); + if (root != null) { + // 递归左(右)子树 + if (root.left != null) { + list.addAll(inorderTraversal2(root.left)); + } + // 把某个根节点的值 加入到list中 + list.add(root.val); + // 递归右(左)子树 + if (root.right != null) { + list.addAll(inorderTraversal2(root.right)); + } + } + return list; + } + + /** + * 使用迭代法 进行中序遍历 + * + * @param root + * @return + */ + private static List inorderTraversal3(TreeNode root) { + List list = new ArrayList(); + + if (root == null) + return list; + + Stack stack = new Stack(); + + // 遍历条件为当前节点不为空 或者 栈不为空 + while (root != null || !stack.isEmpty()) { + // 将当前节点的所有左节点入栈 + // 如果当前节点不为空,递归其左节点 + while (root != null) { + stack.push(root);//将左节点入栈 + System.out.println(String.format("节点%d被放入栈中", root.val)); + root = root.left; + } + + // 从栈中弹出节点 + if (!stack.isEmpty()) { + // 将栈中的节点的第一个弹出 + // 存在左节点的,优先将最深层左节点弹出 + root = stack.pop(); + System.out.println(String.format("节点%d从栈中弹出", root.val)); + list.add(root.val); + System.out.println(String.format("将节点%d放入集合中", root.val)); + // 切到右子树,给下一轮找左子树使用 + root = root.right; + } + } + return list; + } + + private static List inorderTraversal4(TreeNode root) { + List list = new ArrayList<>(); + + if (root == null) + return list; + + Stack stack = new Stack<>(); + + // 中序遍历条件有两个 当前节点不为空 或者 栈不为空 + while (root != null || !stack.isEmpty()) { + + // 将当前节点的所有左边子节点入栈(包括根节点) + while (root != null) { + stack.push(root); + root = root.left; + } + + // 处理栈中的节点 + if (!stack.isEmpty()) { + // 将栈顶节点弹出并放入list + root = stack.pop(); + list.add(root.val); + + // 将节点切到弹出节点的右子节点, 便于下次迭代处理 + root = root.right; + } + } + return list; + } +} diff --git a/src/main/java/com/study/tree/binarytree/InvertTree.java b/src/main/java/com/study/tree/binarytree/InvertTree.java new file mode 100644 index 0000000..db1851f --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/InvertTree.java @@ -0,0 +1,104 @@ +package com.study.tree.binarytree; + +import com.study.utils.TreeUtils; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * 二叉树翻转 + *

+ * 翻转一棵二叉树。 + *

+ * 示例: + *

+ * 输入: + *

+ * 4 + * / \ + * 2 7 + * / \ / \ + * 1 3 6 9 + * 输出: + *

+ * 4 + * / \ + * 7 2 + * / \ / \ + * 9 6 3 1 + *

+ * 链接:https://leetcode-cn.com/problems/invert-binary-tree + */ +public class InvertTree { + + public static void main(String[] args) { + + Integer[] nodes = {4, 2, 7, 1, 3, 6, 9}; + TreeNode root = TreeUtils.buildTree(nodes); + TreeUtils.show(root); + TreeNode newRoot = invertTree(root); + //TreeNode newRoot = invertTree2(root); + TreeUtils.show(newRoot); + } + + + /** + * 后序遍历, 深度优先dfs 自下而上左右节点互换 + *

+ * 时间复杂度O(n) + * + * @param root + * @return + */ + public static TreeNode invertTree(TreeNode root) { + if (root == null) + return null; + + TreeNode left = invertTree(root.left); + TreeNode right = invertTree(root.right); + + // 从最底层叶子节点开始左右互换 + root.left = right; + root.right = left; + + // 返回新的root节点,其左右子节点已经互换 + return root; + } + + /** + * 使用层级遍历的方式 + * + * 时间复杂度O(n) + * @param root + * @return + */ + public static TreeNode invertTree2(TreeNode root) { + if (root == null) + return null; + + Queue queue = new LinkedList(); + + queue.offer(root); + + // 一层一层遍历, 并将左右子节点互换 + while (!queue.isEmpty()) { + int size = queue.size(); + // 当前层级的所有节点取出, 并将其左右子节点进行互换 + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + if (node != null) { + TreeNode tmp = node.left; + node.left = node.right; + node.right = tmp; + + // 左右节点互换后, 再将左右节点依次放入队列中 + if (node.left != null) + queue.offer(node.left); + if (node.right != null) + queue.offer(node.right); + } + } + } + return root; + } +} diff --git a/src/main/java/com/study/tree/binarytree/LevelTraversal.java b/src/main/java/com/study/tree/binarytree/LevelTraversal.java new file mode 100644 index 0000000..b0b7fc9 --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/LevelTraversal.java @@ -0,0 +1,184 @@ +package com.study.tree.binarytree; + +import com.study.utils.TreeUtils; + +import java.util.*; + +/** + * 层级遍历二叉树,广度优先遍历bfs + * 1 + * 2 3 + * 4 5 6 7 + * https://leetcode-cn.com/problems/binary-tree-level-order-traversal/comments/ + *

+ * 解题思路, + * 1.借用一个queue先进先出的结构来遍历二叉树 remove和add方法 + * 2.借用一个stack后进先出的接口来遍历二叉树 pop和push方法 + *

+ * 用宽度优先搜索遍历来划分层次:[[1], [2, 3], [4, 5 , 6 ,7]]。 + * https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/er-cha-shu-de-ceng-ci-bian-li-by-leetcode/ + */ +public class LevelTraversal { + public static void main(String[] args) { + Integer[] arr = {1, 2, 3, 4, 5, 6, 7}; + TreeNode root = TreeUtils.buildTree(arr); + + //List> allNodes = levelOrder(root); + List> allNodes = levelOrder2(root); + + for (List nodesInALevel : allNodes) { + System.out.println(); + for (Integer node : nodesInALevel) { + System.out.print(node + " "); + } + } + } + + /** + * 返回二叉树的所有节点的值 + *

+ * 使用迭代的方式 + *

+ * 思路: 使用一个队列来装载二叉树的每一层节点, 通过先进先出的原理使得取出来的节点可以根据装入的节点顺序一样(根 左 右) + * + * @param root + * @return + */ + private static List> levelOrder(TreeNode root) { + // 创建一个2维的节点集合, 第1维度为层集合,第2维度为每层的节点集合 + List> levels = new ArrayList>(); + //如果根节点为空,直接返回空列表 + if (root == null) return levels; + + // 创建一个当前层的队列 + Queue queue = new LinkedList(); + queue.add(root); + + while (!queue.isEmpty()) { + // 往节点结合中添加当前层级的空节点集合 + List currentLevelNodes = new ArrayList<>(); + // 获取当前层次的节点个数 + int level_length = queue.size(); + // 遍历当前层的节点个数 + for (int i = 0; i < level_length; i++) { + TreeNode node = queue.remove();//取出并移除队列的第一个节点 根据先进先出原则, 取出的顺序为 根 左 右(根据添加时候的顺序一致) + + // 往节点集合中添加当前层级的节点 + currentLevelNodes.add(node.val); + + // 往当前层队列中添加当前层的子节点,用于下一次迭代处理 + if (node.left != null) + queue.add(node.left); + if (node.right != null) + queue.add(node.right); + } + levels.add(currentLevelNodes); + } + return levels; + } + + /** + * 使用 queue + * + * @param root + * @return + */ + private static List> levelOrder2(TreeNode root) { + List> nodes = new ArrayList>(); + + if (root == null) + return nodes; + + Queue queue = new LinkedList(); + queue.add(root); + + // 遍历队列中的节点,当前层里的所有节点 + while (queue.size() > 0) { + List currentLevelNodes = new ArrayList<>(); + + // 当前层节点数 + int nodeCount = queue.size(); + + // 遍历当前层所有的节点, 往节点结合中添加当前层所有节点值, 往队列中添加当前所有节点的子节点 + for (int i = 0; i < nodeCount; i++) { + // 添加当前层节点值 + TreeNode node = queue.remove(); + currentLevelNodes.add(node.val); + + if (node.left != null) + queue.add(node.left); + if (node.right != null) + queue.add(node.right); + } + nodes.add(currentLevelNodes); + } + return nodes; + } + + /** + * 使用递归的方式实现广度优先, 这里在递归的过程中用到了层级控制,如果集合当前层没有节点,就新建; 如果有节点, 就加到当前层里面 + * + * @param root + * @return + */ + private static List> searchByRecursion(TreeNode root) { + List> nodeList = new ArrayList<>(); + if (root == null) + return nodeList; + // 递归获取所有子节点 + search(root, 0, nodeList); + return nodeList; + } + + private static void search(TreeNode root, int level, List> nodeList) { + if (root == null) { + return; + } + // 集合的当前层没有节点, 就新建 + if (level >= nodeList.size()) { + List subList = new ArrayList<>(); + subList.add(root); + nodeList.add(subList); + } else { + // 集合的当前层有节点, 就直接添加 + nodeList.get(level).add(root); + } + search(root.left, level + 1, nodeList); + search(root.right, level + 1, nodeList); + } + + private static List> levelOrder3(TreeNode root) { + List> nodes = new ArrayList>(); + + if (root == null) + return nodes; + + int level = 0; + Queue queue = new LinkedList(); + queue.add(root); + + // 遍历队列中的节点,当前层里的所有节点 + while (queue.size() > 0) { + nodes.add(new ArrayList()); + + // 当前层节点数 + int nodeCount = queue.size(); + + // 遍历当前层所有的节点, 往节点结合中添加当前层所有节点值, 往队列中添加当前所有节点的子节点 + for (int i = 0; i < nodeCount; i++) { + // 添加当前层节点值 + TreeNode node = queue.remove(); + nodes.get(level).add(node.val); + + if (node.left != null) + queue.add(node.left); + if (node.right != null) + queue.add(node.right); + } + + // 接着循环下一层 + level++; + } + return nodes; + } +} diff --git a/src/main/java/com/study/tree/binarytree/LevelTraversal2.java b/src/main/java/com/study/tree/binarytree/LevelTraversal2.java new file mode 100644 index 0000000..bdadd06 --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/LevelTraversal2.java @@ -0,0 +1,82 @@ +package com.study.tree.binarytree; + + +import com.study.utils.TreeUtils; +import sun.reflect.generics.tree.Tree; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + + +/** + * 二叉树遍历2 + * + * 将一个二叉树各节点输出, 奇数行按顺序输出,偶数行倒叙输出 + * input: + * A + * / \ + * B C + * / \ \ + * D E F + * / \ + * G H + * output: + * A + * CB + * DEF + * HG + */ +public class LevelTraversal2 { + + public static void main(String[] args) { + String[] nodes = {"A", "B", "C", "D", "E", null, "F", null,null, "G", null, null, null, null, "H"}; + TreeNode2 root = TreeUtils.buildTree(nodes); + TreeUtils.show(root); + List> list = getTreeList(root); + + int level = 1; + for (List levelList : list) { + // 奇数行 正序输出 + if (level % 2 != 0) { + for (int i = 0; i < levelList.size(); i++) { + System.out.print(levelList.get(i)); + } + } else { + //偶数行 倒叙输出 + for (int i = levelList.size() - 1; i >= 0; i--) { + System.out.print(levelList.get(i)); + } + } + System.out.println(); + level++; + } + } + + private static List> getTreeList(TreeNode2 root) { + List> list = new ArrayList<>(); + + Queue queue = new LinkedList<>(); + queue.offer(root); + + while (!queue.isEmpty()) { + List levelList = new ArrayList<>(); + int size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode2 node = queue.poll(); + levelList.add(node.val); + + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + list.add(levelList); + } + + return list; + } +} diff --git a/src/main/java/com/study/tree/binarytree/LowestCommonAncestor.java b/src/main/java/com/study/tree/binarytree/LowestCommonAncestor.java new file mode 100644 index 0000000..a1e2005 --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/LowestCommonAncestor.java @@ -0,0 +1,123 @@ +package com.study.tree.binarytree; + +import com.study.utils.TreeUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * 二叉树的最近公共祖先 + *

+ * 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 + *

+ * 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” + *

+ * 例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4] + *

+ * 示例 1: + *

+ * 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 + * 输出: 3 + * 解释: 节点 5 和节点 1 的最近公共祖先是节点 3。 + * 示例 2: + *

+ * 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 + * 输出: 5 + * 解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。 + *

+ * 说明: + *

+ * 所有节点的值都是唯一的。 + * p、q 为不同节点且均存在于给定的二叉树中。 + *

+ * https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/ + *

+ * https://time.geekbang.org/course/detail/130-42708 + */ +public class LowestCommonAncestor { + + public static void main(String[] args) { + int[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + List treeNodes = TreeUtils.buildTreeAndList(arr); + //TreeNode p = treeNodes.get(4); + //TreeNode q = treeNodes.get(5); + +// TreeNode p = treeNodes.get(1); +// TreeNode q = treeNodes.get(2); + +// TreeNode p = treeNodes.get(3); +// TreeNode q = treeNodes.get(4); + + TreeNode p = treeNodes.get(1); + TreeNode q = treeNodes.get(7); + TreeNode root = treeNodes.get(0); + + TreeNode lca = getlca(root, p, q); + if (lca == null) { + System.out.println("没有最近公共祖先"); + } else { + System.out.println(lca.val); + } + } + + /** + * 递归的方式查找最近公共祖先 + *

+ * 先从左子树进行查找,如果找到第一个目标节点p, 那么不再往下查找,而是直接从目标节点的父节点r的右子树进行查找 + *

+ * 如果右子树也找到目标节点q,说明r就是p和q的最近公共祖先 + *

+ * 如果右子树没有找到目标节点q, 那么目标节点q肯定在目标节点p的子节点中. 那么p就是p和q的最近公共祖先 + *

+ * 如果左子树没有找到目标节点, 那么再从右子树找, 如果找到第一个目标节点q, 那么q就是p和q的最近公共祖先,因为p肯定是q的子节点 + *

+ * 若果左右子树都没有找到目标节点, 那么直接返回null + * + * @param root + * @param p + * @param q + * @return + */ + private static TreeNode getlca(TreeNode root, TreeNode p, TreeNode q) { + if (root == null) { + System.out.println("递归往内中止返回null值"); + return null; + } + + if (root == p || root == q) { + if (root == p) + System.out.println("递归往内中止, root == p ==" + root.val); + if (root == q) + System.out.println("递归往内中止, root == q ==" + root.val); + return root; + } + + // 遍历左子树看有没有目标节点, 有返回目标节点, 没有返回null + TreeNode left = getlca(root.left, p, q); // 如果找到目标节点, 无需下遍历, 直接从当前节点的右子树进行遍历 + System.out.println("递归往外开始, 左边目标节点left = " + (left == null ? "null" : left.val)); + // 遍历右子树有看没有目标节点, 有返回目标节点, 没有返回null + TreeNode right = getlca(root.right, p, q); + System.out.println("递归往外开始, 右边目标节点right = " + (right == null ? "null" : right.val)); + + // 如果左右子树都有目标节点, 说明公共祖先就是它本身 + if (left != null && right != null) { + System.out.println("左右子树都有目标节点,公共祖先为" + root.val); + return root; + } + + // 如果目标节点都在左子树,返回左子树匹配的节点 + // 如果目标节点都在右子树,返回右子树匹配的节点 + //return left != null ? left : right; + + if (left != null) { + System.out.println("公共祖先在左子树, left = " + left.val); + return left; + } + if (right != null) { + System.out.println("公共祖先在右子树, right = " + right.val); + return right; + } + + return null; + } +} diff --git a/src/main/java/com/study/tree/binarytree/MaximumDepth.java b/src/main/java/com/study/tree/binarytree/MaximumDepth.java new file mode 100644 index 0000000..e5cc071 --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/MaximumDepth.java @@ -0,0 +1,132 @@ +package com.study.tree.binarytree; + +import com.study.utils.TreeUtils; + +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * 二叉树的最大深度 + *

+ * 给定一个二叉树,找出其最大深度。 + *

+ * 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 + *

+ * 说明: 叶子节点是指没有子节点的节点。 + *

+ * 示例: + * 给定二叉树 [3,9,20,null,null,15,7], + *

+ * 3 + * / \ + * 9 20 + * / \ + * 15 7 + * 返回它的最大深度 3 。 + *

+ * 链接:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree + */ +public class MaximumDepth { + + /** + * 可以通过深度优先进行递归 或者 层级遍历 + * + * @param args + */ + public static void main(String[] args) { + //Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + Integer[] arr = {5, 1, 4, null, null, 3, 6}; + TreeNode root = TreeUtils.buildTree(arr); + TreeUtils.show(root); + + //System.out.println(maxDepthByRecursion(root, "根", root.val)); + //System.out.println(maxDepthByLoop(root)); + System.out.println(maxDepthByRecursion2(root)); + } + + /** + * 使用dfs深度优先的策略(递归)的方式来解决这个问题 + *

+ * 从根递归到最底层叶子节点, 值为1, 然后每往上面一层+1,同时每层会对左右两边的值做对比,取最大的. 如果是算最大深度,那就是取根的左子树和右子树中的最大值 + *

+ * 我们每个结点只访问一次,因此时间复杂度为 O(N), + * + * @param root + * @return + */ + private static int maxDepthByRecursion(TreeNode root, String position, int val) { + if (root == null) { + System.out.println(String.format("%s从%d节点到叶子节点递归终止", position, val)); + return 0; + } + + System.out.println(String.format("%s从%d节点到叶子节点递归", position, val)); + + int left = maxDepthByRecursion(root.left, "左边", root.val); + // 左边递归到了叶子节点, 开始往根节点走 + System.out.println(String.format("左边从叶子节点往根节点开始, 当前节点为: %d left = %d", root.val, left)); + + int right = maxDepthByRecursion(root.right, "右边", root.val); + // 右边递归到了叶子节点, 开始往根节点走 + System.out.println(String.format("右边从叶子节点往根节点开始, 当前节点为: %d right = %d", root.val, right)); + + int result = Math.max(left, right) + 1; + System.out.println(String.format("result = %d, left = %d, right = %d", result, left, right)); + + return result; + } + + /** + * 深度优先遍历, 计算每层左右节点的层数,取最大值 + * + * 时间复杂度为O(n) + * + * @param root + * @return + */ + private static int maxDepthByRecursion2(TreeNode root) { + if (root == null) + return 0; + + int left = maxDepthByRecursion2(root.left); + int right = maxDepthByRecursion2(root.right); + + // 从最底层往根节点回溯的过程中, 计算每层的层数, 取左右两边最大的层数并+1(每层的层数) + // 每次回溯到上一层 都会将当前层左右节点的最大层数+1 带给上一层 + return Math.max(left, right) + 1;// 最底层为0+1=1, 倒数第2层为0+1+1=2 + } + + + /** + * 使用广度优先的方式找出最大深度 遍历+队列 + * 时间复杂度为O(n) + * + * @param root + * @return + */ + private static int maxDepthByLoop(TreeNode root) { + if (root == null) + return 0; + + Queue queue = new LinkedList<>(); + queue.offer(root); + int level = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + level++; + } + // 返回最大层级数, 即最深层级 + return level; + } +} diff --git a/src/main/java/com/study/tree/binarytree/MinimumDepth.java b/src/main/java/com/study/tree/binarytree/MinimumDepth.java new file mode 100644 index 0000000..e6fb509 --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/MinimumDepth.java @@ -0,0 +1,145 @@ +package com.study.tree.binarytree; + +import com.study.utils.TreeUtils; +import sun.reflect.generics.tree.Tree; + +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * 二叉树的最小深度 + *

+ * 给定一个二叉树,找出其最小深度。 + *

+ * 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 + *

+ * 说明: 叶子节点是指没有子节点的节点。 + *

+ * 示例: + *

+ * 给定二叉树 [3,9,20,null,null,15,7], + *

+ * 3 + * / \ + * 9 20 + * / \ + * 15 7 + * 返回它的最小深度  2. + *

+ * 链接:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree + */ +public class MinimumDepth { + + public static void main(String[] args) { + //Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + Integer[] arr = {5, 1, 4, null, null, 3}; + //Integer[] arr = {5, 1}; + TreeNode root = TreeUtils.buildTree(arr); + TreeUtils.show(root); + //System.out.println(minDepthByRecursion(root)); + //System.out.println(minDepthByRecursion2(root)); + System.out.println(minDepthByRecursion3(root)); + //System.out.println(minDepthByLoop(root)); + } + + /** + * 使用分治的方法 从左右节点进行递归, + * 注意: 当root节点左右孩子都为空时,返回1 + *

+ * 如果是单边为空或者都为空的节点, 返回另一边+1 这样能够记录所有分支的高度, 最后通过min(跟的左分支,根的右分支) 得到深度最小的 + *

+ * 时间复杂度为O(n) + * + * @param root + * @return + */ + private static int minDepthByRecursion(TreeNode root) { + if (root == null) + return 0; + // 使用分治的方法 从左右节点进行递归 + int left = minDepthByRecursion(root.left); + int right = minDepthByRecursion(root.right); + + // 如果是单边为空或者都为空的节点, 返回另一边+1 这样能够记录所有分支的高度, 最后通过min(跟的左分支,根的右分支) 得到深度最小的 + // 如果左边子节点为空的时候, 那么返回右边不为空的孩子深度+1 + if (left == 0) + return right + 1; + // 如果右边子节点为空的时候, 那么返回左边不为空的孩子深度+1 + if (right == 0) + return left + 1; + + //如果左右子节点都不为空, 返回左右深度最小的那个+1 + return Math.min(left, right) + 1; + } + + + private static int minDepthByRecursion2(TreeNode root) { + if (root == null) + return 0; + + int left = minDepthByRecursion2(root.left); + int right = minDepthByRecursion2(root.right); + + // 记录所有分支的深度, 最后在根节点上取左右最短的一条 + if (left == 0) + return right + 1; + if (right == 0) + return left + 1; + + return Math.min(left, right) + 1; + } + + private static int minDepthByRecursion3(TreeNode root) { + if (root == null) + return 0; + + int left = minDepthByRecursion3(root.left); + int right = minDepthByRecursion3(root.right); + + // 到达叶子节点的时候, 进行回溯的时候计算层级高度 + if (left == 0) + return right + 1; + if (right == 0) + return left + 1; + + return Math.min(left, right) + 1; + } + + /** + * 使用层级遍历的方式: 循环+队列 一层一层遍历并记录层数, 当遇到第一个左右子节点为空的节点, 那这个节点就是最小深度的叶子节点, 返回的层数为最小层数(最小深度). + *

+ * 时间复杂度为O(n), 空间复杂度为O(n) + * + * @param root + * @return + */ + private static int minDepthByLoop(TreeNode root) { + int level = 0; + if (root == null) + return level; + + Queue queue = new LinkedList<>(); + queue.offer(root); + + while (!queue.isEmpty()) { + level++; + int size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + // 如果节点的左右子树都为空, 那么这个节点为叶子节点, 也就是最小深度 + if (node.left == null && node.right == null) { + return level; + } + // 将左右节点按顺序放入队列中,用于下一层遍历 + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + } + return level; + } +} diff --git a/src/main/java/com/study/tree/binarytree/NodesInALevel.java b/src/main/java/com/study/tree/binarytree/NodesInALevel.java new file mode 100644 index 0000000..7f7a9a5 --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/NodesInALevel.java @@ -0,0 +1,165 @@ +package com.study.tree.binarytree; + +import com.study.utils.TreeUtils; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * 求第k层有多少个节点, 根节点为第0层 + *

+ * 我们知道: 一个满二叉树的第k层节点个数n为: n = 2 ^ k (根节点为第0层) + *

+ * 而第k层的节点数与第(k - 1)层的节点数比例为 2 : 1 + *

+ * https://www.cnblogs.com/hapjin/p/5505988.html + */ +public class NodesInALevel { + public static void main(String[] args) { + Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + + TreeNode root = TreeUtils.buildTree(arr); + + int k = 2; +// System.out.printf("第%d层节点数:%d\r\n", k, k_nodes(root, k)); +// System.out.println("分别为:"); + //List nodes = k_nodes2(root, k); + List nodes = k_nodes3(root, k); + for (TreeNode node : nodes) { + System.out.print(node.val + " "); + } + } + + /** + * 满二叉树 + * 1 1 + * 2 3 2 + * 4 5 6 7 4 + * 8 9 10 11 12 13 14 15 8 + *

+ * 非满二叉树 + * 1 1 + * 2 3 2 + * 4 5 6 7 4 + * 8 9 10 11 12 5 + *

+ * 正常情况下每多一层, 节点数都是上一层的两倍, 相当于 两个 上一层节点数相加 即 N(k) = N(k-1) + N(k-1) + *

+ * + * @param root + * @param k + * @return + */ + private static int k_nodes(TreeNode root, int k) { + if (k < 0) { + return 0; + } + // 当root为空或者 递归到root为空, 说明到第k层或到k层之前就已经没有节点了 + if (root == null) { + //System.out.println(0); + return 0; + } + // 当k ==0 或者 递归到 k==0, 说明k层有节点 + if (k == 0) { + // 到达第k层节点数, 返回多少次1, 说明有多少个节点到达了第k层,(即第k层有多少个节点) + //System.out.println(1); + return 1; + } else { + // 递归一次 最多得到 1+1==2个节点, 递归两次 2+2 == 4个节点, 最终递归 k-1词 最多得到 2 ^ (k-1)个数节点 + // 如果中途有空节点, 那么只能算没有空节点的节点个数, 即递归到k==0时候的那些情况节点, 求1出现次数之和即是第k层节点数 + // 从最后一层 k==0的时候开始计算, 如果k==0的时候 返回 + +// int a = k_nodes(root.left, k - 1); +// int b = k_nodes(root.right, k - 1); +// return a + b; + // 先递归完左边子树的直到return返回值,然后往回走向根节点, 在走向根节点同时递归右子树 + // 每递归完一次右子树, 就将左右子树的结果进行相加. 并return给下一次递归相加 + return k_nodes(root.left, k - 1) + k_nodes(root.right, k - 1); + } + } + + /** + * 返回第k层的节点集合 + */ + private static List k_nodes2(TreeNode root, int k) { + List list = new ArrayList(); + if (k < 0) return list; + + //nodes(root, k, list); + nodes2(root, k, list); + return list; + } + + private static void nodes(TreeNode root, int k, List list) { + if (root == null) { + System.out.println("节点为空,递归往内中止"); + return; + } + + if (k == 0) { + // 将第k层节点加入到集合中 + list.add(root); + System.out.println("往集合中添加节点" + root.val); + } + + System.out.println(String.format("递归往内, 当前节点为%d, k=%d", root.val, k)); + + // 递归左(右)子树, 找出k=0节点 + nodes(root.left, k - 1, list); + System.out.println(String.format("递归左子树往外, 当前节点为%d, k=%d", root.val, k)); + + // 递归右(左)子树, 找出k=0节点 + nodes(root.right, k - 1, list); + System.out.println(String.format("递归右子树往外, 当前节点为%d, k=%d", root.val, k)); + } + + private static void nodes2(TreeNode root, int k, List list) { + if (root == null) + return; + + if (k == 0) { + list.add(root); + } + + // 无所谓左右递归顺序 + nodes2(root.right, k - 1, list); + nodes2(root.left, k - 1, list); + } + + /** + * 使用层级遍历的方式获取第k层节点数 + * + * @param root + * @param k + * @return + */ + private static List k_nodes3(TreeNode root, int k) { + List nodes = new ArrayList<>(); + + if (root == null) + return nodes; + + Queue queue = new LinkedList(); + queue.offer(root); + + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + // 获取第k层节点 + if (k == 0) { + nodes.add(node); + } + if (node.left != null) + queue.offer(node.left); + if (node.right != null) + queue.offer(node.right); + } + if (k == 0) break; + k--; + } + return nodes; + } +} diff --git a/src/main/java/com/study/tree/binarytree/PostorderTraversal.java b/src/main/java/com/study/tree/binarytree/PostorderTraversal.java new file mode 100644 index 0000000..bd5924f --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/PostorderTraversal.java @@ -0,0 +1,242 @@ +package com.study.tree.binarytree; + +import com.study.utils.TreeUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * 145. 二叉树的后序遍历 + *

+ * 给定一个二叉树,返回它的 后序 遍历。 + *

+ * 示例: + *

+ * 输入: [1,null,2,3] + * 1 + * \ + * 2 + * / + * 3 + *

+ * 输出: [3,2,1] + * 进阶: 递归算法很简单,你可以通过迭代算法完成吗? + *

+ * https://leetcode-cn.com/problems/binary-tree-postorder-traversal + */ +public class PostorderTraversal { + public static void main(String[] args) { + Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + TreeNode root = TreeUtils.buildTree(arr); + + TreeUtils.show(root); + + //List list = postorderTraversal(root); + //List list = postorderTraversal2(root); + //List list = postorderTraversal3(root); + // List list = postorderTraversal4(root); +// for (Integer num : list) { +// System.out.print(num + " "); +// } + //Stack stack = postorderTraversal5(root); + Stack stack = postorderTraversal5_2(root); + while (!stack.isEmpty()) { + System.out.print(stack.pop() + " "); + } + } + + /** + * 使用递归方法进行后续遍历 + *

+ * 后续遍历 左 - 右 - 根 + * + * @param root + * @return + */ + private static List postorderTraversal(TreeNode root) { + List list = new ArrayList(); + traversal(root, list); + return list; + } + + private static void traversal(TreeNode root, List list) { + if (root != null) { + traversal(root.left, list); + traversal(root.right, list); + list.add(root.val); + } + } + + /** + * 使用递归2 + * + * @param root + * @return + */ + private static List postorderTraversal2(TreeNode root) { + List list = new ArrayList(); + if (root != null) { + if (root.left != null) { + list.addAll(postorderTraversal2(root.left)); + } + + if (root.right != null) { + list.addAll(postorderTraversal2(root.right)); + } + + list.add(root.val); + } + return list; + } + + /** + * 使用迭代法后序遍历 + *

+ * 从右边节点开始处理, 先右再左, 将节点值倒序插到集合中 + * + * @param root + * @return + */ + private static List postorderTraversal3(TreeNode root) { + List list = new ArrayList(); + Stack stack = new Stack(); + + if (root == null) { + return list; + } + + stack.push(root); + System.out.println(String.format("将根节点%d入栈", root.val)); + + while (!stack.isEmpty()) { + root = stack.pop(); + // 将节点值加到列表顶部 + list.add(0, root.val); + System.out.println(String.format("将节点%d加入到集合顶部", root.val)); + + //和前序比那里不一样, 先将左节点入栈 + if (root.left != null) { + stack.push(root.left); + System.out.println(String.format("将左节点%d入栈", root.left.val)); + } + + //再将右节点入栈 + if (root.right != null) { + stack.push(root.right); + System.out.println(String.format("将右节点%d入栈", root.right.val)); + } + } + return list; + } + + /** + * 通过迭代法+栈 实现后序遍历二叉树 + * 遍历出来的每个二叉树节点再使用栈来存储,最后遍历输出 + * 根节点最先进结果栈,最后出结果栈 + * + * @param root + * @return + */ + private static Stack postorderTraversal5(TreeNode root) { + Stack result = new Stack(); + Stack stack = new Stack(); + + if (root == null) { + return result; + } + + stack.push(root); + System.out.println(String.format("将根节点%d入栈", root.val)); + + while (!stack.isEmpty()) { + root = stack.pop(); + // 因为根节点是最后输出的,所以将根节点首先放入结果栈中(先进后出) + result.push(root.val); + System.out.println(String.format("将节点%d加入到栈中", root.val)); + + //和前序比那里不一样, 先将左节点入栈 + if (root.left != null) { + stack.push(root.left); + System.out.println(String.format("将左节点%d入栈", root.left.val)); + } + + //再将右节点入栈 + if (root.right != null) { + stack.push(root.right); + System.out.println(String.format("将右节点%d入栈", root.right.val)); + } + } + return result; + } + + + private static Stack postorderTraversal5_2(TreeNode root) { + Stack output = new Stack<>(); + Stack input = new Stack<>(); + + if (root == null) { + return output; + } + + input.push(root); + + while (!input.isEmpty()) { + TreeNode node = input.pop(); + output.push(node.val); + + if (node.left != null) + input.push(node.left); + + if (node.right != null) + input.push(node.right); + } + + return output; + } + + /** + * 迭代遍历 左 右 根 + *

+ * 利用根节点和子节点的关系,来找出根节点并巧妙处理 + * + * @param root + * @return + */ + private static List postorderTraversal4(TreeNode root) { + List list = new ArrayList(); + Stack stack = new Stack(); + + if (root == null) { + return list; + } + + // 使用前置节点来找出其根节点 + TreeNode pre = null; + + stack.push(root); + while (!stack.isEmpty()) { + // 拿到栈顶上的元素, 不移除 + TreeNode cur = stack.peek(); + + // 如果当前节点没有子节点 或者 当前节点是前置节点的根节点 + if ((cur.left == null && cur.right == null || (pre != null && (pre == cur.left || pre == cur.right)))) { + list.add(cur.val); + // 记录前置节点 + pre = cur; + // 移除已处理的当前节点, 便于下一轮循环处理下一个节点 + stack.pop(); + } else { + // 先右子节点入栈, 再左子节点, 根据后入先出原则, 下一轮循环先处理左子节点 + if (cur.right != null) { + stack.push(cur.right); + } + if (cur.left != null) { + stack.push(cur.left); + } + } + } + + return list; + } +} diff --git a/src/main/java/com/study/tree/binarytree/PreorderTraversal.java b/src/main/java/com/study/tree/binarytree/PreorderTraversal.java new file mode 100644 index 0000000..f7b3b0c --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/PreorderTraversal.java @@ -0,0 +1,174 @@ +package com.study.tree.binarytree; + +import com.study.utils.TreeUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * 144. 二叉树的前序遍历 深度优先遍历dfs + *

+ * 给定一个二叉树,返回它的 前序 遍历。 前序遍历: 根 - 左 - 右 + *

+ * 示例: + *

+ * 输入: [1,null,2,3] + * 1 + * \ + * 2 + * / + * 3 + *

+ * 输出: [1,2,3] + *

+ * 进阶: 递归算法很简单,你可以通过迭代算法完成吗? + *

+ * https://leetcode-cn.com/problems/binary-tree-preorder-traversal/ + */ +public class PreorderTraversal { + public static void main(String[] args) { + Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + TreeNode root = TreeUtils.buildTree(arr); + TreeUtils.show(root); + + //List list = preorderTraversal(root); + List list = preorderTraversalByLoop(root); + // List list = preorderTraversalByLoop2(root); + //List list = preorderTraversalByLoopWithList(root); + for (Integer num : list) { + System.out.print(num + " "); + } + } + + + /** + * 通过递归的方式遍历整个二叉树 时间复杂度O(n) + *

+ * 前序遍历: 根 - 左 - 右 + * + * @param root + * @param list + */ + private static void traversal(TreeNode root, List list) { + if (root != null) { + list.add(root.val); + traversal(root.left, list); + traversal(root.right, list); + } + } + + + public static List preorderTraversal(TreeNode root) { + List list = new ArrayList(); + traversal(root, list); + return list; + } + + /** + * 使用迭代的方式 遍历二叉树 + *

+ * 用到栈后入先出的原理, 将左子树放在右子树后面入栈, 确保左子树先弹出,优先处理左子树,之后再处理右子树 + *

+ * 关于为什么循环中没专门放入根节点, 因为除了最顶层的根节点, 其他层级的根节点都是左右子树 + *

+ * 时间复杂度O(n), 空间复杂度O(n) + * + * @param root + * @return + */ + public static List preorderTraversalByLoop(TreeNode root) { + List list = new ArrayList(); + Stack stack = new Stack(); + + if (root == null) { + return list; + } + + stack.push(root); + System.out.println(String.format("节点%d入栈", root.val)); + + while (!stack.isEmpty()) { + // 每次优先弹出左节点, + // 由于每次左子树都是最后放进去,而每次循环都只pop一次, 存在左子树优先pop左子树,没有再pop右节点 + TreeNode node = stack.pop(); + System.out.println(String.format("节点%d弹出", node.val)); + list.add(node.val); + + // 根据栈后进先出的原理, 把left节点放在right后面放入栈中 + if (node.right != null) { + stack.push(node.right); + System.out.println(String.format("右节点%d入栈", node.right.val)); + } + + if (node.left != null) { + stack.push(node.left); + System.out.println(String.format("左节点%d入栈", node.left.val)); + } + } + return list; + } + + public static List preorderTraversalByLoop2(TreeNode root) { + List nodes = new ArrayList<>(); + + if (root == null) + return nodes; + + Stack stack = new Stack<>(); + stack.push(root); //将节点放到栈顶 + + while (!stack.isEmpty()) { + + // 弹出栈顶的节点 + TreeNode node = stack.pop(); + nodes.add(node.val); + + if (node.right != null) { + stack.push(node.right); + } + + // 根据后进先出的原理,将左子树节点放在后面入栈 + if (node.left != null) { + stack.push(node.left); + } + } + return nodes; + } + + /** + * 使用循环 + 集合 实现前序遍历 + * 每次都将节点放到集合顶部, 并且从集合顶部取出节点 + * + * @param root + * @return + */ + public static List preorderTraversalByLoopWithList(TreeNode root) { + List nodes = new ArrayList<>(); + + if (root == null) + return nodes; + + List list = new ArrayList<>(); + list.add(0, root); //将节点放到集合顶部 + + while (!list.isEmpty()) { + + // 取出并删除集合顶部元素 + TreeNode node = list.remove(0); + nodes.add(node.val); + + if (node.right != null) { + // 将右节点放到集合顶部 + list.add(0, node.right); + } + + // 根据后进先出的原理,将左子树节点放在后面入栈 + if (node.left != null) { + // 将左节点放到集合顶部 + list.add(0, node.left); + } + } + return nodes; + } +} diff --git a/src/main/java/com/study/tree/binarytree/PreorderTraversal2.java b/src/main/java/com/study/tree/binarytree/PreorderTraversal2.java new file mode 100644 index 0000000..59784e4 --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/PreorderTraversal2.java @@ -0,0 +1,59 @@ +package com.study.tree.binarytree; + +import com.study.utils.TreeUtils; + +import java.util.ArrayList; +import java.util.List; + + +/** + * 将二叉树的先序遍历的节点 组装成只有右边节点的二叉树 + * 1 + * 2 3 + * 4 5 6 + *

+ * 1 + * 2 + * 4 + * 5 + * 3 + * 6 + */ +public class PreorderTraversal2 { + + /** + * 两种解法: + * 方法一: 将二叉树前序遍历 取出里面每个节点放入集合, 然后遍历集合,取出里面的节点 + * 方法而: 直接在原来的二叉树上做处理 + * + * @param args + */ + public static void main(String[] args) { + Integer[] arr = {1, 2, 3, 4, 5, 6}; + TreeNode root = TreeUtils.buildTree(arr); + TreeUtils.show(root); + + List list = new ArrayList<>(); + traversal(root, list); + TreeNode newRoot = buildNewTree(list); + TreeUtils.show(newRoot); + } + + private static void traversal(TreeNode root, List list) { + if (root != null) { + TreeNode node = new TreeNode(root.val); + list.add(node); + traversal(root.left, list); + traversal(root.right, list); + } + } + + private static TreeNode buildNewTree(List list) { + TreeNode root = list.get(0); + for (int i = 1; i < list.size(); i++) { + root.right = list.get(i); + root = root.right; + } + return list.get(0); + } +} diff --git a/src/main/java/com/study/tree/binarytree/TraverseTree.java b/src/main/java/com/study/tree/binarytree/TraverseTree.java new file mode 100644 index 0000000..1f082de --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/TraverseTree.java @@ -0,0 +1,145 @@ +package com.study.tree.binarytree; + +import com.study.utils.TreeUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * 遍历二叉树 二叉树有三种遍历模式 前序遍历 中序遍历 后续遍历 + *

+ * 其中每种遍历 又有多种遍历方法 比如递归, 迭代 + */ +public class TraverseTree { + public static void main(String[] args) { + Integer[] arr = {0, 1, 2, 3, 4, 5, 6, 7}; + TreeNode root = TreeUtils.buildTree(arr); + + //preOrderReverse(root); // 0 1 3 7 4 2 5 6 + //preOrderReverse2(root); // 0 1 3 7 4 2 5 6 + //inOrderReverse(root); // 7 3 1 4 0 5 2 6 + inOrderReverse2(root); + //afterOrderReverse(root); //7 3 4 1 5 6 2 0 + } + + /** + * 前序遍历 根在前面 + *

+ * 根 - 左 - 右 + *

+ * 先输出根节点 + */ + private static void preOrderReverse(TreeNode node) { + if (node == null) { + return; + } + + System.out.printf("当前节点----------------%d\r\n", node.val); + + System.out.printf("当前节点%d的左节点为%s\r\n", node.val, node.left == null ? "空" : node.left.val); + preOrderReverse(node.left); + + System.out.printf("当前节点%d的右节点为%s\r\n", node.val, node.right == null ? "空" : node.right.val); + preOrderReverse(node.right); + } + + + /** + * 使用迭代法进行前序遍历 + * + * @param node + */ + private static void preOrderReverse2(TreeNode node) { + Stack stack = new Stack(); + + if (node == null) { + return; + } + + stack.push(node); + + while (!stack.isEmpty()) { + // 先处理根节点 + TreeNode tempNode = stack.pop(); + System.out.println(tempNode.val); + + //根据栈的后进先出原理, 将left节点放在right后面入栈 + if (tempNode.right != null) { + stack.push(tempNode.right); + } + + if (tempNode.left != null) { + stack.push(tempNode.left); + } + } + } + + /** + * 中序遍历 根在中间 + *

+ * 左 - 根 - 右 + *

+ * 根节点在左节点之后输出 + */ + private static void inOrderReverse(TreeNode node) { + if (node == null) { + return; + } + System.out.printf("当前节点%d的左节点为%s\r\n", node.val, node.left == null ? "空" : node.left.val); + inOrderReverse(node.left); + + System.out.printf("当前节点----------------%d\r\n", node.val); + + System.out.printf("当前节点%d的右节点为%s\r\n", node.val, node.right == null ? "空" : node.right.val); + inOrderReverse(node.right); + } + + /** + * 使用迭代法进行中序遍历 + * + * @param node + */ + private static void inOrderReverse2(TreeNode node) { + Stack stack = new Stack(); + + if (node == null) { + return; + } + + while (node != null || !stack.isEmpty()) { + + // 先将当前节点以及所有左节点入栈 + while (node != null) { + stack.push(node); + node = node.left; //左 + } + + if (!stack.isEmpty()) { + node = stack.pop(); //根 + System.out.println(node.val); + node = node.right; //右 + } + } + } + + /** + * 后续遍历 根在末尾 + *

+ * 左 - 右 - 根 + *

+ * 根节点在左右节点输出之后再输出 + */ + private static void afterOrderReverse(TreeNode node) { + if (node == null) { + return; + } + System.out.printf("当前节点%d的左节点为%s\r\n", node.val, node.left == null ? "空" : node.left.val); + afterOrderReverse(node.left); + + System.out.printf("当前节点%d的右节点为%s\r\n", node.val, node.right == null ? "空" : node.right.val); + afterOrderReverse(node.right); + + System.out.printf("当前节点----------------%d\r\n", node.val); + } +} diff --git a/src/main/java/com/study/tree/binarytree/TreeNode.java b/src/main/java/com/study/tree/binarytree/TreeNode.java new file mode 100644 index 0000000..8479394 --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/TreeNode.java @@ -0,0 +1,11 @@ +package com.study.tree.binarytree; + +public class TreeNode { + public TreeNode left; + public TreeNode right; + public Integer val; + + public TreeNode(Integer val) { + this.val = val; + } +} diff --git a/src/main/java/com/study/tree/binarytree/TreeNode2.java b/src/main/java/com/study/tree/binarytree/TreeNode2.java new file mode 100644 index 0000000..2b8e4c3 --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/TreeNode2.java @@ -0,0 +1,11 @@ +package com.study.tree.binarytree; + +public class TreeNode2 { + public TreeNode2 left; + public TreeNode2 right; + public String val; + + public TreeNode2(String val) { + this.val = val; + } +} diff --git a/src/main/java/com/study/tree/binarytree/binarysearchtree/CRUD.java b/src/main/java/com/study/tree/binarytree/binarysearchtree/CRUD.java new file mode 100644 index 0000000..e205ff6 --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/binarysearchtree/CRUD.java @@ -0,0 +1,235 @@ +package com.study.tree.binarytree.binarysearchtree; + +import com.study.tree.binarytree.TreeNode; +import com.study.utils.TreeUtils; + +import java.util.List; + +/** + * 查找二叉搜索树的某个节点 + */ +public class CRUD { + + public static void main(String[] args) { + // 由于二叉搜索树也是二叉树, 所以既可以使用二叉搜索树的方法也可以使用二叉树的方法求最近公共祖先 + int[] arr = {5, 3, 8, 1, 4, 7, 9}; + List treeNodes = TreeUtils.buildTreeAndList(arr); + TreeNode root = treeNodes.get(0); + + TreeUtils.show(root); + + int target = 7; + //int target = 2; + //boolean result = searchByRecursion(root,target); + TreeNode result = searchByRecursion2(root, target); + //TreeNode result = searchByLoop(root, target); + //System.out.println(result != null ? result.val : "未找到"); + + TreeUtils.show(root); + //TreeNode node = insertByRecursion(root, 6); + //TreeNode node = insertByRecursion2(root, 6); + TreeNode node = insertByLoop(root, 6); + System.out.println(node.val); + TreeUtils.show(root); + } + + /** + * 后续遍历查找结果, 需要在递归的时候将参数传进去 + * + * @param root 根节点 + * @param target 目标值 + * @return 是否找到目标 + */ + private static boolean searchByRecursion(TreeNode root, int target) { + if (root == null) { + return false; // 未找到目标 + } + + if (root.val > target) { + // 将找到或者未找到目标不断回溯上去 + return searchByRecursion(root.left, target); + } + + if (root.val < target) { + // 将找到或者未找到目标不断回溯上去 + return searchByRecursion(root.right, target); + } + + // 找到目标 + return true; + } + + /** + * 查找目标节点 + * + * @param root 根节点 + * @param target 目标值 + * @return 目标节点 + */ + private static TreeNode searchByRecursion2(TreeNode root, int target) { + if (root == null) + return null; + + if (root.val > target) { + return searchByRecursion2(root.left, target); + } + + if (root.val < target) { + return searchByRecursion2(root.right, target); + } + + // 后续遍历 查找结果 + return root; + } + + /** + * 先序遍历查找目标节点 + * + * @param root + * @param target + * @return + */ + private static TreeNode searchByRecursion3(TreeNode root, int target) { + // 如果为空, 说明递归到了最底层 叶子节点也没找到 + if (root == null) { + System.out.println("没有找到目标节点"); + return null; + } + + // 如果找到了目标 + if (root.val == target) { + // 一旦找到, 返回结果 + System.out.println("找到了节点" + root.val); + System.out.println("开始回溯"); + return root; + } + + // 如果目标值小于根节点, 往左子树找 + if (root.val > target) { + // 找到目标节点后, 如果曾经从左边找过, 会从左边回溯,并返回结果 + System.out.println("从左子树节点找" + root.left.val); + TreeNode left = searchByRecursion2(root.left, target); + System.out.println("回溯到左子树节点" + root.left.val); + return left; + } else { + // 如果目标值大于根节点, 往右子树找 + System.out.println("从右子树节点找" + root.right.val); + TreeNode right = searchByRecursion2(root.right, target); + System.out.println("回溯到右子树节点" + root.right.val); + return right; + } + } + + /** + * 使用循环的方式,查找目标节点 + * + * @param root + * @param target + * @return + */ + private static TreeNode searchByLoop(TreeNode root, int target) { + while (root != null) { + // 找到目标节点 直接返回 + if (root.val == target) + return root; + + // 目标节点小于根节点 + if (root.val > target) { + // 往左找 + root = root.left; + } else { + // 大于目标节点往右找 + root = root.right; + } + } + // 循环结束的时候,还没找到 返回空 + return null; + } + + /** + * 往树中插入新节点 + *

+ * 首先通过二分查找 找到该节点的位置, 如果节点存在, 直接返回, 如果不存在则创建并返回. + * + * @param root 根节点 + * @param value 要插入的节点 + * @return 根节点 + */ + private static TreeNode insertByRecursion(TreeNode root, int value) { + if (root == null) { + // 如果没找到, 创建该节点 + return new TreeNode(value); + } + + // 从左边找 + if (root.val > value) { + // 将返回的左节点添加到根节点左边, 如果创建了一个新的节点,将被接上 + root.left = insertByRecursion(root.left, value); + } + + if (root.val < value) { + // 从右边找 + // 将返回的右节点添加到根节点右边, 如果创建了一个新的节点,将被接上 + root.right = insertByRecursion(root.right, value); + } + + // 不断回溯到根节点 + return root; + } + + private static TreeNode insertByRecursion2(TreeNode root, int value) { + // 没找到就创建目标节点并返回 + if (root == null) { + return new TreeNode(value); + } + + if (root.val > value) { + // 从左边找 + root.left = insertByRecursion2(root.left, value); + } + if (root.val < value) { + // 从右边找 + root.right = insertByRecursion2(root.right, value); + } + + return root; + } + + /** + * 通过迭代的方式插入新节点 + * + * @param root 根 + * @param val 新节点值 + * @return 根节点 + */ + private static TreeNode insertByLoop(TreeNode root, int val) { + TreeNode result = root; + while (root != null) { + if (root.val > val) { + // 判断左节点是否为空, 为空就说明新节点不存在,则创建 + if (root.left == null) { + root.left = new TreeNode(val); + break; + } + // 往左边找 + root = root.left; + } + if (root.val < val) { + // 判断右节点是否为空, 为空就说明新节点不存在,则创建 + if (root.right == null) { + root.right = new TreeNode(val); + break; + } + // 往右边找 + root = root.right; + } + // 新节点已经存在,停止查找,无需新建 + if (root.val == val) + break; + } + + // 返回根节点 + return result; + } + +} diff --git a/src/main/java/com/study/tree/binarytree/binarysearchtree/LowestCommonAncestor.java b/src/main/java/com/study/tree/binarytree/binarysearchtree/LowestCommonAncestor.java new file mode 100644 index 0000000..5874628 --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/binarysearchtree/LowestCommonAncestor.java @@ -0,0 +1,109 @@ +package com.study.tree.binarytree.binarysearchtree; + +import com.study.tree.binarytree.TreeNode; +import com.study.utils.TreeUtils; + +import java.util.List; + +/** + * 二叉搜索树的最近公共祖先 lowest common ancestor + *

+ * 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 + *

+ * 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。” + *

+ * 例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5] + *

+ * 示例 1: + *

+ * 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 + * 输出: 6 + * 解释: 节点 2 和节点 8 的最近公共祖先是 6。 + * 示例 2: + *

+ * 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4 + * 输出: 2 + * 解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。 + *

+ *

+ * 说明: + *

+ * 所有节点的值都是唯一的。 + * p、q 为不同节点且均存在于给定的二叉搜索树中。 + *

+ * https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/ + */ +public class LowestCommonAncestor { + public static void main(String[] args) { + + // 由于二叉搜索树也是二叉树, 所以既可以使用二叉搜索树的方法也可以使用二叉树的方法求最近公共祖先 + int[] arr = {5, 3, 7, 1, 4, 6, 8}; + List treeNodes = TreeUtils.buildTreeAndList(arr); + TreeNode root = treeNodes.get(0); + + TreeUtils.show(root); + + TreeNode p = treeNodes.get(5); + TreeNode q = treeNodes.get(6); + + //TreeNode lca = getlca(root, p, q); + TreeNode lca = getlca2(root, p, q); + if (lca != null) + System.out.println(lca.val); + } + + /** + * @param root + * @param p + * @param q + * @return + */ + private static TreeNode getlca(TreeNode root, TreeNode p, TreeNode q) { + + if (root == null) + return null; + if (p == root || q == root) + return root; + + // 递归根左子树, 查找p或q节点, 一旦找到立刻退出, 进行根右子树递归 + // 因为根左子树找到1个节点, 那么只要根右子树存在另一个节点, 那最近公共祖先就是根节点, 否则就是左子树找到的节点本身(另一个节点肯定在这个节点下面) + TreeNode left = getlca(root.left, p, q); + + // 递归右子树, 查看右边是否也存在目标节点 + TreeNode right = getlca(root.right, p, q); + + // 如果左右都存在, 直接返回当前节点 + if (left != null && right != null) + return root; + + // 如果左右都没找到, 说明最近公共不存在 + if (left == null && right == null) + return null; + + //如果左边不存在,而右边存在返回右边, 如果右边不存在,左边存在,返回左边 + return left == null ? right : left; + } + + /** + * 使用二叉搜索树 左子树所有节点 < 父节点 < 右子树所有节点 的原理 + * + * 将节点和根节点比较, 从左子树找 或者 右子树找, 不需要全树遍历, 类似二叉查找 + * + * 时间复杂度为O(logN) + * + * @param root + * @param p + * @param q + * @return + */ + private static TreeNode getlca2(TreeNode root, TreeNode p, TreeNode q) { + // 如果 p和q的值 都小于 root根节点, 说明两个节点都在左子树, 直接从树的左边去找 + if (p.val < root.val && root.val > q.val) + return getlca2(root.left, p, q); + // 如果 p和q的值都大于 root, 说明量两个节点都在右子树, 往树的右边去找 + if (p.val > root.val && q.val > root.val) + return getlca2(root.right, p, q); + // 如果 p.val < root.val < q.val 或 p.val > root.val > q.val 则直接返回root节点 + return root; + } +} diff --git a/src/main/java/com/study/tree/binarytree/binarysearchtree/ValidateBinarySearchTree.java b/src/main/java/com/study/tree/binarytree/binarysearchtree/ValidateBinarySearchTree.java new file mode 100644 index 0000000..b497c98 --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/binarysearchtree/ValidateBinarySearchTree.java @@ -0,0 +1,237 @@ +package com.study.tree.binarytree.binarysearchtree; + +import com.study.tree.binarytree.TreeNode; +import com.study.utils.TreeUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * 验证二叉搜索树 又名 二叉排序树/二叉平衡(查找)树 + *

+ * BinarySearchTree BST + * + *

+ * 给定一个二叉树,判断其是否是一个有效的二叉搜索树。 + *

+ * 假设一个二叉搜索树具有如下特征: + *

+ * 节点的左子树只包含小于当前节点的数。 + * 节点的右子树只包含大于当前节点的数。 + * 所有左子树和右子树自身必须也是二叉搜索树。 + * 示例 1: + *

+ * 输入: + * 2 + * / \ + * 1 3 + * 输出: true + * 示例 2: + *

+ * 输入: + * 5 + * / \ + * 1 4 + * / \ + * 3 6 + * 输出: false + * 解释: 输入为: [5,1,4,null,null,3,6]。 + * 根节点的值为 5 ,但是其右子节点值为 4 。 + *

+ * https://leetcode-cn.com/problems/validate-binary-search-tree/ + */ +public class ValidateBinarySearchTree { + /** + * 节点的左子树只包含小于当前节点的数。 + * 节点的右子树只包含大于当前节点的数。 + * 所有左子树和右子树自身必须也是二叉搜索树。 + * + * @param args + */ + public static void main(String[] args) { + //Integer[] arr = {5, 3, 7, 1, 4, 6, 8}; + //Integer[] arr = {1, 1}; + + Integer[] arr = {5, 1, 6, null, null, 3, 7}; + + //Integer[] arr = {5, 1, 9, null, null, 7, 10, null, null, null, null, 8, 3}; + + //Integer[] arr = {3, 9, 20, null, null, 15, 7}; + + TreeNode root = TreeUtils.buildTree(arr); + System.out.println("中序遍历-----"); + List nodes = new ArrayList(); + inorder(root, nodes); // 验证树是否正确,从小到大输出 + + for (TreeNode node : nodes) { + System.out.print(node.val + " "); + } + System.out.println("\r\n中序遍历-----"); + + System.out.println("打印出树的结构-----"); + // 将刚刚创建的树打印出来 + TreeUtils.show(root); + System.out.println("打印出树的结构-----"); + + // 中序遍历获取所有节点 判断是否为有序节点 + //System.out.println(isValidBST_InOrder(root)); + //System.out.println(isValidBST_InOrder_2(root)); + + // 递归的方式判断是否所有子树 都满足 左子树<节点<右子树 + System.out.println(isValidBST(root)); + //System.out.println(isValidBST_2(root)); + + //System.out.println(isValidBST2(root)); + } + + + /** + * 判断该tree是否为一个有效的二叉搜索树 + *

+ * 先使用中序遍历, 将每个节点放到一个arraylist集合中 + *

+ * 然后遍历集合,对比前后两个元素, 看看是不是后面的元素都比前面元素大,如果是则说明集合是从小到大排列 + *

+ * 同理说明二叉搜索树里的节点也是按左 根 右 从小到大排列的 + * + * @param root + * @return + */ + private static boolean isValidBST_InOrder(TreeNode root) { + List nodes = new ArrayList(); + inorder(root, nodes); // 验证树是否正确,从小到大输出 + + for (int i = 0; i < nodes.size() - 1; i++) { + //如果前一个元素大于后一个元素, 则说明不是有效二叉搜索树 + if (nodes.get(i).val >= nodes.get(i + 1).val) { + return false; + } + } + return true; + } + + private static boolean isValidBST_InOrder_2(TreeNode root) { + List nodes = new ArrayList<>(); + // 中序遍历后, 返回的节点为从小到大有序的 + inorder_2(root, nodes); + + for (int i = 0; i < nodes.size() - 1; i++) { + // 如果后面的节点大于前面的节点 说明它不是二叉搜索树 + if (nodes.get(i).val >= nodes.get(i + 1).val) { + return false; + } + } + return true; + } + + /** + * 验证是否为二叉搜索树, 需要判断左边所有与节点值是否小于根节点, 右边所有节点值 大于 根节点. + * 以及每棵子树 都是 左子树 < 节点 < 右子树 + *

+ * 使用深度优先搜索: 需要把最小值min和最大值max传入, 让左子树所有节点与根节点值min作比较, 让右子树所有节点与根节点值max作比较. + * 对于根节点左边所有子树, 最大值为根节点max; 对于根节点右边所有子树,最小值为根节点min; + * + *

+ * 节点的左子树只包含小于当前节点的数。 + * 节点的右子树只包含大于当前节点的数。 + * 所有左子树和右子树自身必须也是二叉搜索树。 + * + * 时间复杂度 : O(N)。每个结点访问一次。 + * 空间复杂度 : O(N)。我们跟进了整棵树。 + * + * @param root + * @return + */ + private static boolean isValidBST(TreeNode root){ + return helper(root,null,null); + } + + private static boolean helper(TreeNode root, Integer min, Integer max) { + if (root == null) //如果root为空 说明当前节点已经递归到最底层了 或者树本身就是空树 + return true; + //如果root小于最小值, 不是二叉搜索树 + if (min != null && root.val < min) { + System.out.println(String.format("不满足的节点: node: %d, min: %d", root.val, min)); + return false; + } + + //如果root大于最大值, 也不是二叉搜索树 + if (max != null && root.val > max) { + System.out.println(String.format("不满足的节点: node: %d, max: %d", root.val, max)); + return false; + } + + //System.out.print(root.val + " "); + + // 是否左子树的所有节点肯定都比根节点小 右子树的所有节点都比根大 + boolean left = helper(root.left, min, root.val); + boolean right = helper(root.right, root.val, max); + + return left && right; //如果左边和右边都满足条件, 则说明该数为二叉搜索树 + //return isValidBST(root.left, min, root.val) && isValidBST(root.right, root.val, max); + } + + private static boolean isValidBST_2(TreeNode root){ + return helper2(root,null,null); + } + + private static boolean helper2(TreeNode root, Integer min, Integer max) { + if (root == null) + return true; + if (min != null && root.val < min) //跟节点不能小于最小值 + return false; + if (max != null && root.val > max) //跟节点也不能大于最大值 + return false; + + return helper2(root.left, min, root.val) && helper2(root.right, root.val, max); + } + + /* 不对的做法, 这只判断了每个子树 是否满足 左子节点 < 节点 < 右子节点, 没有需要判断 左边子树所有节点 < 根节点 < 右边子树所有节点 */ +// /** +// * 验证是否为二叉搜索树, 需要判断左边所有与节点值是否小于根节点, 右边所有节点值 大于 根节点. +// * 不传入根节点值, 但是需要判断值左右节点值是否为空 +// * +// * @param root +// * @return +// */ +// private static boolean isValidBST2(TreeNode root) { +// if (root == null) +// return true; +// +// if (root.left != null && root.left.val != null && root.val < root.left.val) { +// System.out.println(String.format("不满足的左节点: node: %d, root: %d", root.left.val, root.val)); +// return false; +// } +// +// if (root.right != null && root.right.val != null && root.val > root.right.val) { +// System.out.println(String.format("不满足右的节点: node: %d, root: %d", root.right.val, root.val)); +// return false; +// } +// +// return isValidBST2(root.left) && isValidBST2(root.right); +// } + + /** + * 使用中序遍历 获取二叉树每个节点 + *

+ * 左 根 右 + * + * @param root + */ + private static void inorder(TreeNode root, List nodes) { + if (root != null) { + inorder(root.left, nodes); + nodes.add(root); + inorder(root.right, nodes); + } + } + + + private static void inorder_2(TreeNode root, List nodes) { + if (root != null) { + inorder(root.left, nodes); + nodes.add(root); + inorder(root.right, nodes); + } + } +} diff --git a/src/main/java/com/study/tree/trietree/Trie.java b/src/main/java/com/study/tree/trietree/Trie.java new file mode 100644 index 0000000..c1b4231 --- /dev/null +++ b/src/main/java/com/study/tree/trietree/Trie.java @@ -0,0 +1,231 @@ +package com.study.tree.trietree; + +/** + * 字典树 也叫 trie树 + *

+ * 基本性质 + * 1.根节点不包含字符,除根节点外的每一个子节点都包含一个字符 + * 2.从根节点到某一节点。路径上经过的字符连接起来,就是该节点对应的字符串 + * 3.每个节点的所有子节点包含的字符都不相同 + *

+ * 应用场景 + * 典型应用是用于统计,排序和保存大量的字符串(不仅限于字符串),经常被搜索引擎系统用于文本词频统计。 + *

+ * 优点 + * 利用字符串的公共前缀来减少查询时间,最大限度的减少无谓的字符串比较,查询效率比哈希树高。 + *

+ * 遍历算法: bfs深度优先遍历 先根再子节点 + * + *

+ * https://www.cnblogs.com/xujian2014/p/5614724.html + *

+ * https://leetcode-cn.com/problems/implement-trie-prefix-tree/ + */ +public class Trie { + public static void main(String[] args) { + TrieNode root = new TrieNode(); + String[] strs = {"banana", "band", "bee", "absolute", "acm",}; + String[] prefix = {"ba", "b", "band", "abc",}; +// String[] strs = {"北京大学", "北京科技大学", "南京北京路", "南京大学", "南京金陵十二拆",}; +// String[] prefix = {"北", "北京", "北京大学", "南京", "上海浦东新区",}; + for (String str : strs) { + insert(str, root); + } + + System.out.println("------打印字典树中所有单词--------"); + printAllWords(root, "#"); + System.out.println("--------------------------------"); + + // 是否包含这个单词 + System.out.println("------判断是否有ba这个单词--------"); + System.out.println(containsWord("ba", root)); + //System.out.println(containsWord("北京", root)); + System.out.println("--------------------------------"); + + // 打印包含该前缀的单词 + System.out.println("-------打印所有包含ba前缀的单词----"); + System.out.println(hasPrefix("ba", root)); + System.out.println("--------------------------------"); + + //preTraverse(root); + preTraverse2(root); + System.out.println(); + for (String pre : prefix) { + int num = countPrefix(pre, root); + System.out.println(pre + " 数量:" + num); + } + } + + /** + * 在字典树中插入一个单词 + * + * @param str + */ + public static void insert(String str, TrieNode root) { + if (str == null || str.length() == 0) { + return; + } + TrieNode node = root; + char[] letters = str.toCharArray();//将目标单词转换为char数组 + for (int i = 0, len = str.length(); i < len; i++) { + //计算每个char的位置(哈希值), 这样在当前层级查找该元素或节点的的时间复杂度为O(1) + int pos = letters[i] - 'a'; + if (node.son[pos] == null) { //如果当前节点的儿子节点中没有该字符,则构建一个TrieNode并复值该字符 + node.son[pos] = new TrieNode(); + node.son[pos].val = letters[i]; + } else { //如果已经存在,则将由根至该儿子节点组成的字符串模式出现的次数+1 + node.son[pos].num++; + } + node = node.son[pos]; + } + node.isEnd = true; + } + + /** + * 计算拥有该前缀的单词 + * + * @param prefix + * @return + */ + public static int countPrefix(String prefix, TrieNode root) { + if (prefix == null || prefix.length() == 0) { + return -1; + } + TrieNode node = root; + char[] letters = prefix.toCharArray(); + for (int i = 0, len = prefix.length(); i < len; i++) { + int pos = letters[i] - 'a'; + if (node.son[pos] == null) { + return 0; + } else { + node = node.son[pos]; + } + } + return node.num; + } + + /** + * 找出并打印出拥有指定前缀的所有单词 + * + * @param prefix + * @return + */ + public static String hasPrefix(String prefix, TrieNode root) { + if (prefix == null || prefix.length() == 0) { + return null; + } + TrieNode node = root; + char[] letters = prefix.toCharArray(); + for (int i = 0, len = prefix.length(); i < len; i++) { + int pos = letters[i] - 'a'; + if (node.son[pos] == null) { + return null; + } else { + node = node.son[pos]; + } + } + preTraverse(node, prefix); + return null; + } + + /** + * 打印经过此节点的所有单词. + * + * @param root + * @param prefix + */ + public static void preTraverse(TrieNode root, String prefix) { + if (!root.isEnd) { + for (TrieNode child : root.son) { + if (child != null) { + preTraverse(child, prefix + child.val); + } + } + return; + } + System.out.println(prefix); + } + + /** + * 在字典树中查找一个完全匹配的单词 + * + * @param str + * @return + */ + public static boolean containsWord(String str, TrieNode root) { + if (str == null || str.length() == 0) { + return false; + } + TrieNode node = root; + char[] letters = str.toCharArray(); + for (int i = 0, len = str.length(); i < len; i++) { + int pos = letters[i] - 'a'; + if (node.son[pos] != null) { + node = node.son[pos]; + } else { + return false; + } + } + //走到这一步,表明可能完全匹配,可能部分匹配,如果最后一个字符节点为末端节点,则是完全匹配,否则是部分匹配 + return node.isEnd; + } + + /** + * 打印字典树里面所有字符char + *

+ * 前序遍历字典树. bfs深度优先遍历 + * + *

+ * 根 - 所有子节点 + * + * @param root + */ + public static void preTraverse(TrieNode root) { + if (root == null) { + return; + } + System.out.print(root.val + ":" + root.num + " "); + for (TrieNode child : root.son) { + preTraverse(child); + } + } + + /** + * 打印字典树里面所有字符char + * + * @param root + */ + public static void preTraverse2(TrieNode root) { + if (root == null) { + return; + } + for (TrieNode child : root.son) { + if (child != null) { + System.out.print(child.val + ":" + child.num + " "); + if (child.isEnd) + System.out.println(); + } + preTraverse2(child); + } + } + + /** + * 打印字典树里所有的单词 + * 将递归走的每个char进行拼接,直到单词尾部,然后输出这个单词 + * + * @param root 根节点 + * @param prefix 前缀 + */ + private static void printAllWords(TrieNode root, String prefix) { + if (root != null && root.isEnd) { + System.out.println(prefix); + return; + } + if (root == null) + return; + for (TrieNode child : root.son) { + if (child != null) + printAllWords(child, prefix + child.val); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/study/tree/trietree/TrieNode.java b/src/main/java/com/study/tree/trietree/TrieNode.java new file mode 100644 index 0000000..aaa51ca --- /dev/null +++ b/src/main/java/com/study/tree/trietree/TrieNode.java @@ -0,0 +1,27 @@ +package com.study.tree.trietree; + +/** + * 字典树节点 + */ +public class TrieNode +{ + /** + * 中文 + */ + //private int SIZE = 65536; + + /** + * 英文 一共26个字母 + */ + private int SIZE = 256; + + public int num;// 有多少单词通过这个节点,即由根至该节点组成的字符串模式出现的次数 + public TrieNode[] son = new TrieNode[SIZE];// 所有的儿子节点 + public boolean isEnd;// 是不是最后一个节点 + public char val = '#';// 节点的值 + + public TrieNode() { + num = 1; + isEnd = false; + } +} \ No newline at end of file From 6c4a2b835cc2bc7772d3164197580f2f6dc08afa Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 2 Mar 2020 21:04:38 +0800 Subject: [PATCH 69/74] =?UTF-8?q?=E4=BA=A4=E6=8D=A2=E6=95=B0=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/array/SwapNumbersLcci.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/main/java/com/study/array/SwapNumbersLcci.java diff --git a/src/main/java/com/study/array/SwapNumbersLcci.java b/src/main/java/com/study/array/SwapNumbersLcci.java new file mode 100644 index 0000000..1445af5 --- /dev/null +++ b/src/main/java/com/study/array/SwapNumbersLcci.java @@ -0,0 +1,63 @@ +package com.study.array; + +/** + * 交换数字 + *

+ * 编写一个函数,不用临时变量,直接交换numbers = [a, b]中a与b的值。 + *

+ * 示例: + *

+ * 输入: numbers = [1,2] + * 输出: [2,1] + * 提示: + *

+ * numbers.length == 2 + *

+ * https://leetcode-cn.com/problems/swap-numbers-lcci/ + */ +public class SwapNumbersLcci { + + public static void main(String[] args) { + //int[] arr = {1, 2}; + int[] arr = {5, 3}; + //int[] result = swapNumbers(arr); + int[] result = swapNumbers2(arr); + System.out.println(result[0] + " " + result[1]); + } + + + /** + * 求和相减法 + *

+ * 由于数组中只有两个元素, 可以使用两数之和 减去其中一个数 得到另一个数来处理 + * + * @param numbers + * @return + */ + private static int[] swapNumbers(int[] numbers) { + + if (numbers == null || numbers.length != 2) { + return new int[]{}; + } + + numbers[0] = numbers[0] + numbers[1]; // 3 = 1 + 2 + numbers[1] = numbers[0] - numbers[1]; // 1 = 3 - 2 这个时候2变成了1 + numbers[0] = numbers[0] - numbers[1]; // 2 = 3 - 1 这个时候1变成了2 + + return numbers; + } + + /** + * 使用位算法 + * + * @param numbers + * @return + */ + private static int[] swapNumbers2(int[] numbers) { + numbers[0] = numbers[0] ^ numbers[1]; // 3 = 1 ^ 2 + numbers[1] = numbers[0] ^ numbers[1]; // 1 = 3 ^ 2 + numbers[0] = numbers[0] ^ numbers[1]; // 2 = 3 ^ 1 + + return numbers; + } +} From e1c50f538ddf0ce2e7f8ec728d8985b15507ee71 Mon Sep 17 00:00:00 2001 From: longwu Date: Mon, 2 Mar 2020 21:44:10 +0800 Subject: [PATCH 70/74] =?UTF-8?q?=E5=8F=8D=E8=BD=AC=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/string/ReverseString.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/main/java/com/study/string/ReverseString.java diff --git a/src/main/java/com/study/string/ReverseString.java b/src/main/java/com/study/string/ReverseString.java new file mode 100644 index 0000000..5fc53cb --- /dev/null +++ b/src/main/java/com/study/string/ReverseString.java @@ -0,0 +1,43 @@ +package com.study.string; + +/** + * 反转字符串 + *

+ * 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。 + *

+ * 不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 + *

+ * 你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。 + *

+ * https://leetcode-cn.com/problems/reverse-string/ + */ +public class ReverseString { + + public static void main(String[] args) { + char[] chars = {'h', 'e', 'l', 'l', 'o'}; + reverse(chars); + + for (char c : chars) { + System.out.print(c + " "); + } + } + + /** + * 使用前后指针 从首位开始将前后两个元素进行互换 + * + * @param chars + * @return + */ + private static void reverse(char[] chars) { + int left = 0; + int right = chars.length - 1; + // 将前后两个字符进行对调 + while (left < right) { // 如果是奇数, 中间元素不用换位置 + char tmp = chars[left]; + chars[left] = chars[right]; + chars[right] = tmp; + left++; + right--; + } + } +} From d34d7fa355c714e131156eb2e4dbc91615fd0473 Mon Sep 17 00:00:00 2001 From: longwu Date: Tue, 3 Mar 2020 22:35:51 +0800 Subject: [PATCH 71/74] =?UTF-8?q?=E7=BF=BB=E8=BD=AC=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=E9=87=8C=E7=9A=84=E5=8D=95=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/string/ReverseWordsInAString.java | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 src/main/java/com/study/string/ReverseWordsInAString.java diff --git a/src/main/java/com/study/string/ReverseWordsInAString.java b/src/main/java/com/study/string/ReverseWordsInAString.java new file mode 100644 index 0000000..a2e8f55 --- /dev/null +++ b/src/main/java/com/study/string/ReverseWordsInAString.java @@ -0,0 +1,110 @@ +package com.study.string; + + +/** + * 翻转字符串里的单词 + *

+ * 给定一个字符串,逐个翻转字符串中的每个单词。 + *

+ *   + *

+ * 示例 1: + *

+ * 输入: "the sky is blue" + * 输出: "blue is sky the" + * 示例 2: + *

+ * 输入: "  hello world!  " + * 输出: "world! hello" + * 解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 + * 示例 3: + *

+ * 输入: "a good   example" + * 输出: "example good a" + * 解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 + *   + *

+ * 说明: + *

+ * 无空格字符构成一个单词。 + * 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。 + * 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。 + *

+ * https://leetcode-cn.com/problems/reverse-words-in-a-string/ + */ +public class ReverseWordsInAString { + + /** + * @param args + */ + public static void main(String[] args) { + //String s = "the sky is blue"; + String s = "hello world!"; + //s = reverse(s); + s = reverse2(s); + System.out.println(s); + } + + /** + * 方法一: 先用trim()去除字符串首位的空格, 并分割成单词数组 + * 将单词数组反向遍历 放入新字符串中, 从第一个单词之后放入空格 + * + * @param s + * @return + */ + private static String reverse(String s) { + // 将字符串首位去除空格 + // 并分割成单词数组 + String[] words = s.trim().split(" "); + + String result = ""; + // 将单词数组反向遍历 放入新字符串中 + for (int i = words.length - 1; i >= 0; i--) { + if (i != words.length - 1) { + result += " ";// 从第一个单词之后放入空格 + } + result += words[i]; + } + return result; + } + + + /** + * 方法二: + *

+ * 先用trim()去除字符串首位的空格, 将整个字符串进行翻转, 翻转后再将每个单词进行翻转 + * + * @param s + * @return + */ + private static String reverse2(String s) { + s = s.trim(); + + // 将字符串进行翻转, 可以使用前后指针进行对调 + char[] chars = s.toCharArray(); + int left = 0; + int right = chars.length - 1; + + // 将字符串里的字符进行翻转 + while (left < right) { + char tmp = chars[left]; + chars[left] = chars[right]; + chars[right] = tmp; + left++; + right--; + } + + // 将每个单词进行翻转 + String result = ""; + String newString = new String(chars); + String[] words = newString.split(" "); + for (int i = 0; i < words.length; i++) { + for (int j = words[i].length() -1; j >= 0; j--) { + result += words[i].charAt(j); + } + if (i != words.length - 1) + result += " "; //最后一个单词末尾没有空格 + } + return result; + } +} From 21ce23c4cca88795093b0a1eb435da716a1729d8 Mon Sep 17 00:00:00 2001 From: longwu Date: Thu, 5 Mar 2020 11:20:13 +0800 Subject: [PATCH 72/74] =?UTF-8?q?=E6=9F=A5=E6=89=BE=E4=BA=8C=E5=8F=89?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E6=A0=91=E4=B8=AD=E7=AC=ACk=E4=B8=AA?= =?UTF-8?q?=E5=B0=8F=E7=9A=84=E7=BB=93=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../binarysearchtree/KSmallestNode.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/main/java/com/study/tree/binarytree/binarysearchtree/KSmallestNode.java diff --git a/src/main/java/com/study/tree/binarytree/binarysearchtree/KSmallestNode.java b/src/main/java/com/study/tree/binarytree/binarysearchtree/KSmallestNode.java new file mode 100644 index 0000000..2f9fd8b --- /dev/null +++ b/src/main/java/com/study/tree/binarytree/binarysearchtree/KSmallestNode.java @@ -0,0 +1,56 @@ +package com.study.tree.binarytree.binarysearchtree; + +import com.study.tree.binarytree.TreeNode; +import com.study.utils.TreeUtils; + +import java.util.List; + +/** + * 查找二叉搜索树中第k个小的结点 + */ +public class KSmallestNode { + + public static void main(String[] args) { + int[] arr = {5, 3, 7, 1, 4, 6, 8}; + List treeNodes = TreeUtils.buildTreeAndList(arr); + TreeNode root = treeNodes.get(0); + + TreeUtils.show(root); + + int k = 6; + TreeNode kNode = getNodes(root, k); + // 获取第k个节点 + System.out.println(kNode.val); + } + + private static TreeNode getNodes(TreeNode root, int k) { + // 中序遍历获取搜索二叉树 + return helper(root, k); + } + + // 计数器需要设置成全局, 避免放入递归调用栈 + private static int count = 0; + + private static TreeNode helper(TreeNode root, int k) { + if (root == null) { + return null; + } + + TreeNode left = helper(root.left, k); + if (left != null) // 不为空返回是为了继续执行下面的count++ 返回第k个目标root节点, 否则root==null的之后 直接就返回null结束递归了 + return left; + + count++; // 每次遍历完一个节点 k-1, 直到k==0 获取第k个节点 + if (count == k) { + return root; // 返回目标节点, 进行回溯 + } + + TreeNode right = helper(root.right, k); + if (right != null) + return right; + + return null; //没有找到, 可能k不在树中 + } +} + + From 8c6a0b7e316896ede1c4cae480cb124f3fb326a0 Mon Sep 17 00:00:00 2001 From: longwu Date: Wed, 18 Mar 2020 23:32:51 +0800 Subject: [PATCH 73/74] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/array/MaximumSubarray.java | 19 +- .../study/linkedlist/DeleteRepeatedNode.java | 83 +++++++ .../java/com/study/number/KthLargest.java | 18 +- src/main/java/com/study/number/ThreeSum.java | 105 ++++----- src/main/java/com/study/number/TwoSum.java | 4 +- .../java/com/study/search/BinarySearch.java | 35 +-- .../java/com/study/search/BinarySearch2.java | 209 ++---------------- src/main/java/com/study/search/FindSmall.java | 4 +- src/main/java/com/study/search/MinAbs.java | 64 ------ src/main/java/com/study/sort/QuickSort.java | 47 ++-- src/main/java/com/study/sort/SelectSort.java | 48 +++- .../study/string/FirstUniqueCharacter.java | 35 +++ .../com/study/string/GenerateParentheses.java | 42 +++- .../com/study/string/JewelsAndStones.java | 22 +- .../study/string/LongestNumberInString.java | 23 +- .../com/study/string/LongestPalindrome.java | 24 +- .../study/string/ReverseWordsInAString.java | 3 - .../java/com/study/string/ToUpperCase.java | 2 + .../study/string/UniqueEmailAddresses.java | 2 +- .../java/com/study/tree/binarytree/BFS.java | 2 +- .../tree/binarytree/BalancedBinaryTree.java | 8 +- .../java/com/study/tree/binarytree/DFS.java | 2 +- .../tree/binarytree/LevelTraversal2.java | 1 - .../ntree/NAryTreePostorderTraversal.java | 35 +++ src/main/java/com/study/utils/TreeUtils.java | 4 +- 25 files changed, 427 insertions(+), 414 deletions(-) create mode 100644 src/main/java/com/study/linkedlist/DeleteRepeatedNode.java delete mode 100644 src/main/java/com/study/search/MinAbs.java create mode 100644 src/main/java/com/study/string/FirstUniqueCharacter.java create mode 100644 src/main/java/com/study/tree/ntree/NAryTreePostorderTraversal.java diff --git a/src/main/java/com/study/array/MaximumSubarray.java b/src/main/java/com/study/array/MaximumSubarray.java index 006b104..fe96f1e 100644 --- a/src/main/java/com/study/array/MaximumSubarray.java +++ b/src/main/java/com/study/array/MaximumSubarray.java @@ -19,10 +19,11 @@ public class MaximumSubarray { public static void main(String[] args) { - int[] nums = {-2, 1}; - //int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; + //int[] nums = {-2, 1}; + int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; //int result = maxSubArray(nums); - int result = maxSubArray_greedy(nums); + //int result = maxSubArray_greedy(nums); + int result = maxSubArray_greedy_2(nums); //int result = maxSubArray2(nums); System.out.println(result); } @@ -97,6 +98,18 @@ private static int maxSubArray_greedy(int[] nums) { return max; } + private static int maxSubArray_greedy_2(int[] nums) { + int max = nums[0]; + int currentSum = nums[0]; + for(int i =1; i 1->2 + * 输出: 1->2 + * 示例 2: + * + * 输入: 1->1->2->3->3 + * 输出: 1->2->3 + * + * https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list + */ +public class DeleteRepeatedNode { + + public static void main(String[] args) { + LinkedNode node1 = new LinkedNode(1); + LinkedNode node2 = new LinkedNode(2); + LinkedNode node3 = new LinkedNode(2); + LinkedNode node4 = new LinkedNode(3); + LinkedNode node5 = new LinkedNode(3); + + node1.next = node2; + node2.next = node3; + node3.next = node4; + node4.next = node5; + + LinkedListUtils.printLinkedList(node1); + + //LinkedNode result = deleteDuplicates(node1); + LinkedNode result = deleteDuplicates2(node1); + LinkedListUtils.printLinkedList(result); + } + + /** + * 遍历链表, 如果当前节点和下一个节点值相同, 就把下一个节点的指针指向下下一个, + * 去重后再下轮检查一下当前节点是否和下一个节点相同,如果相同就继续指向下下一个去重, 当前节点相同的节点直到去重结束. + * 然后指向当前节点指向下一个节点继续遍历. + * + * 时间复杂度O(n), 空间复杂度O(1) + * + * @param head + * @return + */ + public static LinkedNode deleteDuplicates(LinkedNode head) { + LinkedNode cur = head; + // 遍历条件是当前节点和下一个节点不为空 + while(cur!=null && cur.next !=null){ + // 如果当前节点和下一个节点值相同, 就把下一个节点的指针指向下下一个 + // 2->2->3 + if (cur.val == cur.next.val){ + // 不断将指针指向下下个 进行去重, 直到去重结束 + // 2->2->3 变成了 2->3 //然后下一轮循环 判断 当前2不等于next3, 于是就执行cur = cur.next + cur.next = cur.next.next; // 将下一个指针指向下下一个, 这样下一轮遍历 当前值和下一个值可能就不再一样了,但也可能一样,那就再尝试下一个,直到不一样 + }else{ + cur = cur.next; // 如果当前和下一个节点不相同, 那就直接切换到下一个节点继续遍历 + } + } + + return head; + } + + public static LinkedNode deleteDuplicates2(LinkedNode head) { + LinkedNode cur = head; + while(cur!=null && cur.next!=null){ + if(cur.val == cur.next.val){ + cur.next = cur.next.next; // 不断移除与当前节点相同的重复节点 + }else{ + cur = cur.next;// 往当前节点的下一个节点遍历 + } + } + return head; + } +} + + diff --git a/src/main/java/com/study/number/KthLargest.java b/src/main/java/com/study/number/KthLargest.java index 09878dd..2748351 100644 --- a/src/main/java/com/study/number/KthLargest.java +++ b/src/main/java/com/study/number/KthLargest.java @@ -54,7 +54,7 @@ public int add(int element) { queue.offer(element);// 将元素加到末尾 } else { // 如果队列中的元素满了, 则判断队列最小的元素(顶端的元素)是否比新元素小 - // 如果最小的元素比该元素小, 那将顶端最小的元素移除, 将新元素插入 + // 如果要放入的元素大于顶部元素,则移除顶部元素,放入新元素 if (queue.peek() < element) { queue.poll(); //移除顶端最小元素 queue.offer(element);//插入新元素 @@ -81,18 +81,20 @@ public int add2(int element){ /** * PriorityQueue 优先队列会将里面的元素进行从小到大排序, 每次插入一个新元素后,都会将最小的元素放到最上面 *

- * 优先队列的作用是能保证每次取出的元素都是队列中权值最小的 + * 优先队列的作用是能保证每次取出的元素都是队列中最小的 + * + * 只有大于队列顶部最小的元素才能放入, 同时移除最小的元素. 这样确保新来元素后,队列顶部的元素始终是第k小的 */ public static void main(String[] args) { int k = 3; int[] arr = {4, 5, 8, 2}; - KthLargest kthLargest = new KthLargest(k, arr); + KthLargest kthLargest = new KthLargest(k, arr); //会自动移除第三大的2 // 每次加入新元素都返回第k大的元素 - System.out.println(kthLargest.add(3)); // 第3大的元素为4 堆中元素为 4 5 8 - System.out.println(kthLargest.add(5)); // 第3大的元素为5 堆中元素为 5 8 5 - System.out.println(kthLargest.add(10)); // 第3大的元素为5 堆中元素为 5 8 5 - System.out.println(kthLargest.add(9)); // 第3大的元素为8 堆中元素为 8 10 9 - System.out.println(kthLargest.add(4)); // 第3大的元素为8 堆中元素为 8 10 9 + System.out.println(kthLargest.add(3)); // 3放入失败 第3大的元素为4 堆中元素为 4 5 8 + System.out.println(kthLargest.add(5)); // 5放入尾部 4被移除 第3大的元素为5 堆中元素为 5 8 5 + System.out.println(kthLargest.add(10)); // 10放入尾部 5移除 第3大的元素为5 堆中元素为 5 8 10 + System.out.println(kthLargest.add(9)); // 9放入尾部 5移除 第3大的元素为8 堆中元素为 8 10 9 + System.out.println(kthLargest.add(4)); // 4放入失败 第3大的元素为8 堆中元素为 8 10 9 } } diff --git a/src/main/java/com/study/number/ThreeSum.java b/src/main/java/com/study/number/ThreeSum.java index a223a90..de7113a 100644 --- a/src/main/java/com/study/number/ThreeSum.java +++ b/src/main/java/com/study/number/ThreeSum.java @@ -25,11 +25,12 @@ public class ThreeSum { public static void main(String[] args) { //int[] nums = {-1, 0, 1, 2, -4}; //int[] nums = {-1, 0, 1}; - int[] nums = {-1, 0, 1, 2, -2, -4}; + //int[] nums = {-1, 0, 1, 2, -2, -4}; + // int[] nums = {-1, 0, 1, 2, -1, -4}; + int[] nums = {0, -4, -1, -4, -2, -3, 2}; //int[] nums = {82597, -9243, 62390, 83030, -97960, -26521, -61011, 83390, -38677, 12333, 75987, 46091, 83794, 19355, -71037, -6242, -28801, 324, 1202, -90885, -2989, -95597, -34333, 35528, 5680, 89093, -90606, 50360, -29393, -27012, 53313, 65213, 99818, -82405, -41661, -3333, -51952, 72135, -1523, 26377, 74685, 96992, 92263, 15929, 5467, -99555, -43348, -41689, -60383, -3990, 32165, 65265, -72973, -58372, 12741, -48568, -46596, 72419, -1859, 34153, 62937, 81310, -61823, -96770, -54944, 8845, -91184, 24208, -29078, 31495, 65258, 14198, 85395, 70506, -40908, 56740, -12228, -40072, 32429, 93001, 68445, -73927, 25731, -91859, -24150, 10093, -60271, -81683, -18126, 51055, 48189, -6468, 25057, 81194, -58628, 74042, 66158, -14452, -49851, -43667, 11092, 39189, -17025, -79173, 13606, 83172, 92647, -59741, 19343, -26644, -57607, 82908, -20655, 1637, 80060, 98994, 39331, -31274, -61523, 91225, -72953, 13211, -75116, -98421, -41571, -69074, 99587, 39345, 42151, -2460, 98236, 15690, -52507, -95803, -48935, -46492, -45606, -79254, -99851, 52533, 73486, 39948, -7240, 71815, -585, -96252, 90990, -93815, 93340, -71848, 58733, -14859, -83082, -75794, -82082, -24871, -15206, 91207, -56469, -93618, 67131, -8682, 75719, 87429, -98757, -7535, -24890, -94160, 85003, 33928, 75538, 97456, -66424, -60074, -8527, -28697, -22308, 2246, -70134, -82319, -10184, 87081, -34949, -28645, -47352, -83966, -60418, -15293, -53067, -25921, 55172, 75064, 95859, 48049, 34311, -86931, -38586, 33686, -36714, 96922, 76713, -22165, -80585, -34503, -44516, 39217, -28457, 47227, -94036, 43457, 24626, -87359, 26898, -70819, 30528, -32397, -69486, 84912, -1187, -98986, -32958, 4280, -79129, -65604, 9344, 58964, 50584, 71128, -55480, 24986, 15086, -62360, -42977, -49482, -77256, -36895, -74818, 20, 3063, -49426, 28152, -97329, 6086, 86035, -88743, 35241, 44249, 19927, -10660, 89404, 24179, -26621, -6511, 57745, -28750, 96340, -97160, -97822, -49979, 52307, 79462, 94273, -24808, 77104, 9255, -83057, 77655, 21361, 55956, -9096, 48599, -40490, -55107, 2689, 29608, 20497, 66834, -34678, 23553, -81400, -66630, -96321, -34499, -12957, -20564, 25610, -4322, -58462, 20801, 53700, 71527, 24669, -54534, 57879, -3221, 33636, 3900, 97832, -27688, -98715, 5992, 24520, -55401, -57613, -69926, 57377, -77610, 20123, 52174, 860, 60429, -91994, -62403, -6218, -90610, -37263, -15052, 62069, -96465, 44254, 89892, -3406, 19121, -41842, -87783, -64125, -56120, 73904, -22797, -58118, -4866, 5356, 75318, 46119, 21276, -19246, -9241, -97425, 57333, -15802, 93149, 25689, -5532, 95716, 39209, -87672, -29470, -16324, -15331, 27632, -39454, 56530, -16000, 29853, 46475, 78242, -46602, 83192, -73440, -15816, 50964, -36601, 89758, 38375, -40007, -36675, -94030, 67576, 46811, -64919, 45595, 76530, 40398, 35845, 41791, 67697, -30439, -82944, 63115, 33447, -36046, -50122, -34789, 43003, -78947, -38763, -89210, 32756, -20389, -31358, -90526, -81607, 88741, 86643, 98422, 47389, -75189, 13091, 95993, -15501, 94260, -25584, -1483, -67261, -70753, 25160, 89614, -90620, -48542, 83889, -12388, -9642, -37043, -67663, 28794, -8801, 13621, 12241, 55379, 84290, 21692, -95906, -85617, -17341, -63767, 80183, -4942, -51478, 30997, -13658, 8838, 17452, -82869, -39897, 68449, 31964, 98158, -49489, 62283, -62209, -92792, -59342, 55146, -38533, 20496, 62667, 62593, 36095, -12470, 5453, -50451, 74716, -17902, 3302, -16760, -71642, -34819, 96459, -72860, 21638, 47342, -69897, -40180, 44466, 76496, 84659, 13848, -91600, -90887, -63742, -2156, -84981, -99280, 94326, -33854, 92029, -50811, 98711, -36459, -75555, 79110, -88164, -97397, -84217, 97457, 64387, 30513, -53190, -83215, 252, 2344, -27177, -92945, -89010, 82662, -11670, 86069, 53417, 42702, 97082, 3695, -14530, -46334, 17910, 77999, 28009, -12374, 15498, -46941, 97088, -35030, 95040, 92095, -59469, -24761, 46491, 67357, -66658, 37446, -65130, -50416, 99197, 30925, 27308, 54122, -44719, 12582, -99525, -38446, -69050, -22352, 94757, -56062, 33684, -40199, -46399, 96842, -50881, -22380, -65021, 40582, 53623, -76034, 77018, -97074, -84838, -22953, -74205, 79715, -33920, -35794, -91369, 73421, -82492, 63680, -14915, -33295, 37145, 76852, -69442, 60125, -74166, 74308, -1900, -30195, -16267, -60781, -27760, 5852, 38917, 25742, -3765, 49097, -63541, 98612, -92865, -30248, 9612, -8798, 53262, 95781, -42278, -36529, 7252, -27394, -5021, 59178, 80934, -48480, -75131, -54439, -19145, -48140, 98457, -6601, -51616, -89730, 78028, 32083, -48904, 16822, -81153, -8832, 48720, -80728, -45133, -86647, -4259, -40453, 2590, 28613, 50523, -4105, -27790, -74579, -17223, 63721, 33489, -47921, 97628, -97691, -14782, -65644, 18008, -93651, -71266, 80990, -76732, -47104, 35368, 28632, 59818, -86269, -89753, 34557, -92230, -5933, -3487, -73557, -13174, -43981, -43630, -55171, 30254, -83710, -99583, -13500, 71787, 5017, -25117, -78586, 86941, -3251, -23867, -36315, 75973, 86272, -45575, 77462, -98836, -10859, 70168, -32971, -38739, -12761, 93410, 14014, -30706, -77356, -85965, -62316, 63918, -59914, -64088, 1591, -10957, 38004, 15129, -83602, -51791, 34381, -89382, -26056, 8942, 5465, 71458, -73805, -87445, -19921, -80784, 69150, -34168, 28301, -68955, 18041, 6059, 82342, 9947, 39795, 44047, -57313, 48569, 81936, -2863, -80932, 32976, -86454, -84207, 33033, 32867, 9104, -16580, -25727, 80157, -70169, 53741, 86522, 84651, 68480, 84018, 61932, 7332, -61322, -69663, 76370, 41206, 12326, -34689, 17016, 82975, -23386, 39417, 72793, 44774, -96259, 3213, 79952, 29265, -61492, -49337, 14162, 65886, 3342, -41622, -62659, -90402, -24751, 88511, 54739, -21383, -40161, -96610, -24944, -602, -76842, -21856, 69964, 43994, -15121, -85530, 12718, 13170, -13547, 69222, 62417, -75305, -81446, -38786, -52075, -23110, 97681, -82800, -53178, 11474, 35857, 94197, -58148, -23689, 32506, 92154, -64536, -73930, -77138, 97446, -83459, 70963, 22452, 68472, -3728, -25059, -49405, 95129, -6167, 12808, 99918, 30113, -12641, -26665, 86362, -33505, 50661, 26714, 33701, 89012, -91540, 40517, -12716, -57185, -87230, 29914, -59560, 13200, -72723, 58272, 23913, -45586, -96593, -26265, -2141, 31087, 81399, 92511, -34049, 20577, 2803, 26003, 8940, 42117, 40887, -82715, 38269, 40969, -50022, 72088, 21291, -67280, -16523, 90535, 18669, 94342, -39568, -88080, -99486, -20716, 23108, -28037, 63342, 36863, -29420, -44016, 75135, 73415, 16059, -4899, 86893, 43136, -7041, 33483, -67612, 25327, 40830, 6184, 61805, 4247, 81119, -22854, -26104, -63466, 63093, -63685, 60369, 51023, 51644, -16350, 74438, -83514, 99083, 10079, -58451, -79621, 48471, 67131, -86940, 99093, 11855, -22272, -67683, -44371, 9541, 18123, 37766, -70922, 80385, -57513, -76021, -47890, 36154, 72935, 84387, -92681, -88303, -7810, 59902, -90, -64704, -28396, -66403, 8860, 13343, 33882, 85680, 7228, 28160, -14003, 54369, -58893, 92606, -63492, -10101, 64714, 58486, 29948, -44679, -22763, 10151, -56695, 4031, -18242, -36232, 86168, -14263, 9883, 47124, 47271, 92761, -24958, -73263, -79661, -69147, -18874, 29546, -92588, -85771, 26451, -86650, -43306, -59094, -47492, -34821, -91763, -47670, 33537, 22843, 67417, -759, 92159, 63075, 94065, -26988, 55276, 65903, 30414, -67129, -99508, -83092, -91493, -50426, 14349, -83216, -76090, 32742, -5306, -93310, -60750, -60620, -45484, -21108, -58341, -28048, -52803, 69735, 78906, 81649, 32565, -86804, -83202, -65688, -1760, 89707, 93322, -72750, 84134, 71900, -37720, 19450, -78018, 22001, -23604, 26276, -21498, 65892, -72117, -89834, -23867, 55817, -77963, 42518, 93123, -83916, 63260, -2243, -97108, 85442, -36775, 17984, -58810, 99664, -19082, 93075, -69329, 87061, 79713, 16296, 70996, 13483, -74582, 49900, -27669, -40562, 1209, -20572, 34660, 83193, 75579, 7344, 64925, 88361, 60969, 3114, 44611, -27445, 53049, -16085, -92851, -53306, 13859, -33532, 86622, -75666, -18159, -98256, 51875, -42251, -27977, -18080, 23772, 38160, 41779, 9147, 94175, 99905, -85755, 62535, -88412, -52038, -68171, 93255, -44684, -11242, -104, 31796, 62346, -54931, -55790, -70032, 46221, 56541, -91947, 90592, 93503, 4071, 20646, 4856, -63598, 15396, -50708, 32138, -85164, 38528, -89959, 53852, 57915, -42421, -88916, -75072, 67030, -29066, 49542, -71591, 61708, -53985, -43051, 28483, 46991, -83216, 80991, -46254, -48716, 39356, -8270, -47763, -34410, 874, -1186, -7049, 28846, 11276, 21960, -13304, -11433, -4913, 55754, 79616, 70423, -27523, 64803, 49277, 14906, -97401, -92390, 91075, 70736, 21971, -3303, 55333, -93996, 76538, 54603, -75899, 98801, 46887, 35041, 48302, -52318, 55439, 24574, 14079, -24889, 83440, 14961, 34312, -89260, -22293, -81271, -2586, -71059, -10640, -93095, -5453, -70041, 66543, 74012, -11662, -52477, -37597, -70919, 92971, -17452, -67306, -80418, 7225, -89296, 24296, 86547, 37154, -10696, 74436, -63959, 58860, 33590, -88925, -97814, -83664, 85484, -8385, -50879, 57729, -74728, -87852, -15524, -91120, 22062, 28134, 80917, 32026, 49707, -54252, -44319, -35139, 13777, 44660, 85274, 25043, 58781, -89035, -76274, 6364, -63625, 72855, 43242, -35033, 12820, -27460, 77372, -47578, -61162, -70758, -1343, -4159, 64935, 56024, -2151, 43770, 19758, -30186, -86040, 24666, -62332, -67542, 73180, -25821, -27826, -45504, -36858, -12041, 20017, -24066, -56625, -52097, -47239, -90694, 8959, 7712, -14258, -5860, 55349, 61808, -4423, -93703, 64681, -98641, -25222, 46999, -83831, -54714, 19997, -68477, 66073, 51801, -66491, 52061, -52866, 79907, -39736, -68331, 68937, 91464, 98892, 910, 93501, 31295, -85873, 27036, -57340, 50412, 21, -2445, 29471, 71317, 82093, -94823, -54458, -97410, 39560, -7628, 66452, 39701, 54029, 37906, 46773, 58296, 60370, -61090, 85501, -86874, 71443, -72702, -72047, 14848, 34102, 77975, -66294, -36576, 31349, 52493, -70833, -80287, 94435, 39745, -98291, 84524, -18942, 10236, 93448, 50846, 94023, -6939, 47999, 14740, 30165, 81048, 84935, -19177, -13594, 32289, 62628, -90612, -542, -66627, 64255, 71199, -83841, -82943, -73885, 8623, -67214, -9474, -35249, 62254, -14087, -90969, 21515, -83303, 94377, -91619, 19956, -98810, 96727, -91939, 29119, -85473, -82153, -69008, 44850, 74299, -76459, -86464, 8315, -49912, -28665, 59052, -69708, 76024, -92738, 50098, 18683, -91438, 18096, -19335, 35659, 91826, 15779, -73070, 67873, -12458, -71440, -46721, 54856, 97212, -81875, 35805, 36952, 68498, 81627, -34231, 81712, 27100, -9741, -82612, 18766, -36392, 2759, 41728, 69743, 26825, 48355, -17790, 17165, 56558, 3295, -24375, 55669, -16109, 24079, 73414, 48990, -11931, -78214, 90745, 19878, 35673, -15317, -89086, 94675, -92513, 88410, -93248, -19475, -74041, -19165, 32329, -26266, -46828, -18747, 45328, 8990, -78219, -25874, -74801, -44956, -54577, -29756, -99822, -35731, -18348, -68915, -83518, -53451, 95471, -2954, -13706, -8763, -21642, -37210, 16814, -60070, -42743, 27697, -36333, -42362, 11576, 85742, -82536, 68767, -56103, -63012, 71396, -78464, -68101, -15917, -11113, -3596, 77626, -60191, -30585, -73584, 6214, -84303, 18403, 23618, -15619, -89755, -59515, -59103, -74308, -63725, -29364, -52376, -96130, 70894, -12609, 50845, -2314, 42264, -70825, 64481, 55752, 4460, -68603, -88701, 4713, -50441, -51333, -77907, 97412, -66616, -49430, 60489, -85262, -97621, -18980, 44727, -69321, -57730, 66287, -92566, -64427, -14270, 11515, -92612, -87645, 61557, 24197, -81923, -39831, -10301, -23640, -76219, -68025, 92761, -76493, 68554, -77734, -95620, -11753, -51700, 98234, -68544, -61838, 29467, 46603, -18221, -35441, 74537, 40327, -58293, 75755, -57301, -7532, -94163, 18179, -14388, -22258, -46417, -48285, 18242, -77551, 82620, 250, -20060, -79568, -77259, 82052, -98897, -75464, 48773, -79040, -11293, 45941, -67876, -69204, -46477, -46107, 792, 60546, -34573, -12879, -94562, 20356, -48004, -62429, 96242, 40594, 2099, 99494, 25724, -39394, -2388, -18563, -56510, -83570, -29214, 3015, 74454, 74197, 76678, -46597, 60630, -76093, 37578, -82045, -24077, 62082, -87787, -74936, 58687, 12200, -98952, 70155, -77370, 21710, -84625, -60556, -84128, 925, 65474, -15741, -94619, 88377, 89334, 44749, 22002, -45750, -93081, -14600, -83447, 46691, 85040, -66447, -80085, 56308, 44310, 24979, -29694, 57991, 4675, -71273, -44508, 13615, -54710, 23552, -78253, -34637, 50497, 68706, 81543, -88408, -21405, 6001, -33834, -21570, -46692, -25344, 20310, 71258, -97680, 11721, 59977, 59247, -48949, 98955, -50276, -80844, -27935, -76102, 55858, -33492, 40680, 66691, -33188, 8284, 64893, -7528, 6019, -85523, 8434, -64366, -56663, 26862, 30008, -7611, -12179, -70076, 21426, -11261, -36864, -61937, -59677, 929, -21052, 3848, -20888, -16065, 98995, -32293, -86121, -54564, 77831, 68602, 74977, 31658, 40699, 29755, 98424, 80358, -69337, 26339, 13213, -46016, -18331, 64713, -46883, -58451, -70024, -92393, -4088, 70628, -51185, 71164, -75791, -1636, -29102, -16929, -87650, -84589, -24229, -42137, -15653, 94825, 13042, 88499, -47100, -90358, -7180, 29754, -65727, -42659, -85560, -9037, -52459, 20997, -47425, 17318, 21122, 20472, -23037, 65216, -63625, -7877, -91907, 24100, -72516, 22903, -85247, -8938, 73878, 54953, 87480, -31466, -99524, 35369, -78376, 89984, -15982, 94045, -7269, 23319, -80456, -37653, -76756, 2909, 81936, 54958, -12393, 60560, -84664, -82413, 66941, -26573, -97532, 64460, 18593, -85789, -38820, -92575, -43663, -89435, 83272, -50585, 13616, -71541, -53156, 727, -27644, 16538, 34049, 57745, 34348, 35009, 16634, -18791, 23271, -63844, 95817, 21781, 16590, 59669, 15966, -6864, 48050, -36143, 97427, -59390, 96931, 78939, -1958, 50777, 43338, -51149, 39235, -27054, -43492, 67457, -83616, 37179, 10390, 85818, 2391, 73635, 87579, -49127, -81264, -79023, -81590, 53554, -74972, -83940, -13726, -39095, 29174, 78072, 76104, 47778, 25797, -29515, -6493, -92793, 22481, -36197, -65560, 42342, 15750, 97556, 99634, -56048, -35688, 13501, 63969, -74291, 50911, 39225, 93702, -3490, -59461, -30105, -46761, -80113, 92906, -68487, 50742, 36152, -90240, -83631, 24597, -50566, -15477, 18470, 77038, 40223, -80364, -98676, 70957, -63647, 99537, 13041, 31679, 86631, 37633, -16866, 13686, -71565, 21652, -46053, -80578, -61382, 68487, -6417, 4656, 20811, 67013, -30868, -11219, 46, 74944, 14627, 56965, 42275, -52480, 52162, -84883, -52579, -90331, 92792, 42184, -73422, -58440, 65308, -25069, 5475, -57996, 59557, -17561, 2826, -56939, 14996, -94855, -53707, 99159, 43645, -67719, -1331, 21412, 41704, 31612, 32622, 1919, -69333, -69828, 22422, -78842, 57896, -17363, 27979, -76897, 35008, 46482, -75289, 65799, 20057, 7170, 41326, -76069, 90840, -81253, -50749, 3649, -42315, 45238, -33924, 62101, 96906, 58884, -7617, -28689, -66578, 62458, 50876, -57553, 6739, 41014, -64040, -34916, 37940, 13048, -97478, -11318, -89440, -31933, -40357, -59737, -76718, -14104, -31774, 28001, 4103, 41702, -25120, -31654, 63085, -3642, 84870, -83896, -76422, -61520, 12900, 88678, 85547, 33132, -88627, 52820, 63915, -27472, 78867, -51439, 33005, -23447, -3271, -39308, 39726, -74260, -31874, -36893, 93656, 910, -98362, 60450, -88048, 99308, 13947, 83996, -90415, -35117, 70858, -55332, -31721, 97528, 82982, -86218, 6822, 25227, 36946, 97077, -4257, -41526, 56795, 89870, 75860, -70802, 21779, 14184, -16511, -89156, -31422, 71470, 69600, -78498, 74079, -19410, 40311, 28501, 26397, -67574, -32518, 68510, 38615, 19355, -6088, -97159, -29255, -92523, 3023, -42536, -88681, 64255, 41206, 44119, 52208, 39522, -52108, 91276, -70514, 83436, 63289, -79741, 9623, 99559, 12642, 85950, 83735, -21156, -67208, 98088, -7341, -27763, -30048, -44099, -14866, -45504, -91704, 19369, 13700, 10481, -49344, -85686, 33994, 19672, 36028, 60842, 66564, -24919, 33950, -93616, -47430, -35391, -28279, 56806, 74690, 39284, -96683, -7642, -75232, 37657, -14531, -86870, -9274, -26173, 98640, 88652, 64257, 46457, 37814, -19370, 9337, -22556, -41525, 39105, -28719, 51611, -93252, 98044, -90996, 21710, -47605, -64259, -32727, 53611, -31918, -3555, 33316, -66472, 21274, -37731, -2919, 15016, 48779, -88868, 1897, 41728, 46344, -89667, 37848, 68092, -44011, 85354, -43776, 38739, -31423, -66330, 65167, -22016, 59405, 34328, -60042, 87660, -67698, -59174, -1408, -46809, -43485, -88807, -60489, 13974, 22319, 55836, -62995, -37375, -4185, 32687, -36551, -75237, 58280, 26942, -73756, 71756, 78775, -40573, 14367, -71622, -77338, 24112, 23414, -7679, -51721, 87492, 85066, -21612, 57045, 10673, -96836, 52461, -62218, -9310, 65862, -22748, 89906, -96987, -98698, 26956, -43428, 46141, 47456, 28095, 55952, 67323, -36455, -60202, -43302, -82932, 42020, 77036, 10142, 60406, 70331, 63836, 58850, -66752, 52109, 21395, -10238, -98647, -41962, 27778, 69060, 98535, -28680, -52263, -56679, 66103, -42426, 27203, 80021, 10153, 58678, 36398, 63112, 34911, 20515, 62082, -15659, -40785, 27054, 43767, -20289, 65838, -6954, -60228, -72226, 52236, -35464, 25209, -15462, -79617, -41668, -84083, 62404, -69062, 18913, 46545, 20757, 13805, 24717, -18461, -47009, -25779, 68834, 64824, 34473, 39576, 31570, 14861, -15114, -41233, 95509, 68232, 67846, 84902, -83060, 17642, -18422, 73688, 77671, -26930, 64484, -99637, 73875, 6428, 21034, -73471, 19664, -68031, 15922, -27028, 48137, 54955, -82793, -41144, -10218, -24921, -28299, -2288, 68518, -54452, 15686, -41814, 66165, -72207, -61986, 80020, 50544, -99500, 16244, 78998, 40989, 14525, -56061, -24692, -94790, 21111, 37296, -90794, 72100, 70550, -31757, 17708, -74290, 61910, 78039, -78629, -25033, 73172, -91953, 10052, 64502, 99585, -1741, 90324, -73723, 68942, 28149, 30218, 24422, 16659, 10710, -62594, 94249, 96588, 46192, 34251, 73500, -65995, -81168, 41412, -98724, -63710, -54696, -52407, 19746, 45869, 27821, -94866, -76705, -13417, -61995, -71560, 43450, 67384, -8838, -80293, -28937, 23330, -89694, -40586, 46918, 80429, -5475, 78013, 25309, -34162, 37236, -77577, 86744, 26281, -29033, -91813, 35347, 13033, -13631, -24459, 3325, -71078, -75359, 81311, 19700, 47678, -74680, -84113, 45192, 35502, 37675, 19553, 76522, -51098, -18211, 89717, 4508, -82946, 27749, 85995, 89912, -53678, -64727, -14778, 32075, -63412, -40524, 86440, -2707, -36821, 63850, -30883, 67294, -99468, -23708, 34932, 34386, 98899, 29239, -23385, 5897, 54882, 98660, 49098, 70275, 17718, 88533, 52161, 63340, 50061, -89457, 19491, -99156, 24873, -17008, 64610, -55543, 50495, 17056, -10400, -56678, -29073, -42960, -76418, 98562, -88104, -96255, 10159, -90724, 54011, 12052, 45871, -90933, -69420, 67039, 37202, 78051, -52197, -40278, -58425, 65414, -23394, -1415, 6912, -53447, 7352, 17307, -78147, 63727, 98905, 55412, -57658, -32884, -44878, 22755, 39730, 3638, 35111, 39777, 74193, 38736, -11829, -61188, -92757, 55946, -71232, -63032, -83947, 39147, -96684, -99233, 25131, -32197, 24406, -55428, -61941, 25874, -69453, 64483, -19644, -68441, 12783, 87338, -48676, 66451, -447, -61590, 50932, -11270, 29035, 65698, -63544, 10029, 80499, -9461, 86368, 91365, -81810, -71914, -52056, -13782, 44240, -30093, -2437, 24007, 67581, -17365, -69164, -8420, -69289, -29370, 48010, 90439, 13141, 69243, 50668, 39328, 61731, 78266, -81313, 17921, -38196, 55261, 9948, -24970, 75712, -72106, 28696, 7461, 31621, 61047, 51476, 56512, 11839, -96916, -82739, 28924, -99927, 58449, 37280, 69357, 11219, -32119, -62050, -48745, -83486, -52376, 42668, 82659, 68882, 38773, 46269, -96005, 97630, 25009, -2951, -67811, 99801, 81587, -79793, -18547, -83086, 69512, 33127, -92145, -88497, 47703, 59527, 1909, 88785, -88882, 69188, -46131, -5589, -15086, 36255, -53238, -33009, 82664, 53901, 35939, -42946, -25571, 33298, 69291, 53199, 74746, -40127, -39050, 91033, 51717, -98048, 87240, 36172, 65453, -94425, -63694, -30027, 59004, 88660, 3649, -20267, -52565, -67321, 34037, 4320, 91515, -56753, 60115, 27134, 68617, -61395, -26503, -98929, -8849, -63318, 10709, -16151, 61905, -95785, 5262, 23670, -25277, 90206, -19391, 45735, 37208, -31992, -92450, 18516, -90452, -58870, -58602, 93383, 14333, 17994, 82411, -54126, -32576, 35440, -60526, -78764, -25069, -9022, -394, 92186, -38057, 55328, -61569, 67780, 77169, 19546, -92664, -94948, 44484, -13439, 83529, 27518, -48333, 72998, 38342, -90553, -98578, -76906, 81515, -16464, 78439, 92529, 35225, -39968, -10130, -7845, -32245, -74955, -74996, 67731, -13897, -82493, 33407, 93619, 59560, -24404, -57553, 19486, -45341, 34098, -24978, -33612, 79058, 71847, 76713, -95422, 6421, -96075, -59130, -28976, -16922, -62203, 69970, 68331, 21874, 40551, 89650, 51908, 58181, 66480, -68177, 34323, -3046, -49656, -59758, 43564, -10960, -30796, 15473, -20216, 46085, -85355, 41515, -30669, -87498, 57711, 56067, 63199, -83805, 62042, 91213, -14606, 4394, -562, 74913, 10406, 96810, -61595, 32564, 31640, -9732, 42058, 98052, -7908, -72330, 1558, -80301, 34878, 32900, 3939, -8824, 88316, 20937, 21566, -3218, -66080, -31620, 86859, 54289, 90476, -42889, -15016, -18838, 75456, 30159, -67101, 42328, -92703, 85850, -5475, 23470, -80806, 68206, 17764, 88235, 46421, -41578, 74005, -81142, 80545, 20868, -1560, 64017, 83784, 68863, -97516, -13016, -72223, 79630, -55692, 82255, 88467, 28007, -34686, -69049, -41677, 88535, -8217, 68060, -51280, 28971, 49088, 49235, 26905, -81117, -44888, 40623, 74337, -24662, 97476, 79542, -72082, -35093, 98175, -61761, -68169, 59697, -62542, -72965, 59883, -64026, -37656, -92392, -12113, -73495, 98258, 68379, -21545, 64607, -70957, -92254, -97460, -63436, -8853, -19357, -51965, -76582, 12687, -49712, 45413, -60043, 33496, 31539, -57347, 41837, 67280, -68813, 52088, -13155, -86430, -15239, -45030, 96041, 18749, -23992, 46048, 35243, -79450, 85425, -58524, 88781, -39454, 53073, -48864, -82289, 39086, 82540, -11555, 25014, -5431, -39585, -89526, 2705, 31953, -81611, 36985, -56022, 68684, -27101, 11422, 64655, -26965, -63081, -13840, -91003, -78147, -8966, 41488, 1988, 99021, -61575, -47060, 65260, -23844, -21781, -91865, -19607, 44808, 2890, 63692, -88663, -58272, 15970, -65195, -45416, -48444, -78226, -65332, -24568, 42833, -1806, -71595, 80002, -52250, 30952, 48452, -90106, 31015, -22073, 62339, 63318, 78391, 28699, 77900, -4026, -76870, -45943, 33665, 9174, -84360, -22684, -16832, -67949, -38077, -38987, -32847, 51443, -53580, -13505, 9344, -92337, 26585, 70458, -52764, -67471, -68411, -1119, -2072, -93476, 67981, 40887, -89304, -12235, 41488, 1454, 5355, -34855, -72080, 24514, -58305, 3340, 34331, 8731, 77451, -64983, -57876, 82874, 62481, -32754, -39902, 22451, -79095, -23904, 78409, -7418, 77916}; //List> resultList = threeSum(nums); - //List> resultList = threeSum2(nums); - List> resultList = threeSum3(nums); + List> resultList = threeSum_2(nums); for (List list : resultList) { for (Integer num : list) { System.out.print(num + " "); @@ -39,31 +40,64 @@ public static void main(String[] args) { } private static List> threeSum(int[] nums) { + List> result = new ArrayList<>(); + + if (nums == null || nums.length == 0) { + return result; + } + + // 对数组进行排序, 确保可以使用第2和第3个元素前后双指针 Arrays.sort(nums); - List> ls = new ArrayList>(); + // 遍历第一个元素 for (int i = 0; i < nums.length - 2; i++) { - if (i == 0 || (i > 0 && nums[i] != nums[i - 1])) { // 跳过可能重复的答案 + // 第2个元素从第一个的下一个开始 + int left = i + 1; + // 第3个元素从后面开始 + int right = nums.length - 1; + while (left < right) { + // 符合条件的 + if (nums[left] + nums[right] == -nums[i]) { + result.add(Arrays.asList(nums[i], nums[left], nums[right])); + left++; //之后将left++, 再找满足条件的right + } else { + right--;//如果right过大不满足,那就往前挪动变小一些 + } + } + } + return result; + } + + private static List> threeSum_2(int[] nums) { + List> result = new ArrayList<>(); + if (nums == null || nums.length == 0) { + return result; + } + // 数组排序后方可使用双指针 + Arrays.sort(nums); + + // 枚举第一个元素, 并对另外的两个元素设置左右两个指针 + for (int i = 0; i < nums.length - 2; i++) {//由于left 和 right元素在第一个元素右边,所以第一个元素只需要遍历数组长度-3个即可 + int left = i + 1; //左边指针位置 + int right = nums.length - 1; //右边指针位置 - int l = i + 1, r = nums.length - 1, sum = 0 - nums[i]; - while (l < r) { - if (nums[l] + nums[r] == sum) { - ls.add(Arrays.asList(nums[i], nums[l], nums[r])); - while (l < r && nums[l] == nums[l + 1]) l++; - while (l < r && nums[r] == nums[r - 1]) r--; - l++; - r--; - } else if (nums[l] + nums[r] < sum) { - while (l < r && nums[l] == nums[l + 1]) l++; // 跳过重复值 - l++; - } else { - while (l < r && nums[r] == nums[r - 1]) r--; - r--; + while (left < right) { + // 满足条件的 + if (nums[left] + nums[right] == -nums[i]) { + System.out.println(nums[i] + " " + nums[left] + " " + nums[right]); + List elements = Arrays.asList(nums[i], nums[left], nums[right]); + // 过滤重复的元素 + if (!result.contains(elements)) { + result.add(elements); } + left++; // 再讲left往右移, 找下一个满足条件的 + } else { + // 如果right不满足条件, 比结果target 那就往前挪动一位,尝试下一个 + right--; } } } - return ls; + return result; } @@ -93,37 +127,6 @@ private static List> threeSum2(int[] nums) { return resultList; } - - private static List> threeSum3(int[] nums) { - List> result = new ArrayList<>(); - - if (nums == null || nums.length == 0) { - return result; - } - - // 对数组进行排序, 确保可以使用第2和第3个元素前后双指针 - Arrays.sort(nums); - - // 遍历第一个元素 - for (int i = 0; i < nums.length - 2; i++) { - // 第2个元素从第一个的下一个开始 - int left = i + 1; - // 第3个元素从后面开始 - int right = nums.length - 1; - while (left < right) { - // 符合条件的 - if (nums[left] + nums[right] == -nums[i]) { - result.add(Arrays.asList(nums[i], nums[left], nums[right])); - left++; //之后将left++, 再找满足条件的right - } else { - right--;//如果right过大不满足,那就往前挪动变小一些 - } - } - } - return result; - } - - /** * 判断resultList是否有一条集合包含a,b,c3个元素 * diff --git a/src/main/java/com/study/number/TwoSum.java b/src/main/java/com/study/number/TwoSum.java index 4904026..64bd610 100644 --- a/src/main/java/com/study/number/TwoSum.java +++ b/src/main/java/com/study/number/TwoSum.java @@ -76,10 +76,12 @@ private static int[] twoSum3(int[] nums, int target) { int complement = target - nums[i]; //将目标元素减去当前元素求得差值元素 // 查看hashmap里面是否存在这个差值元素, //if (map.containsKey(complement) && map.get(complement) != i) { //并且这个差值元素不能是nums[i]本身, 实际来说map是不会存在nums[i]的, 因为往map里面添加nums[i]是在最后面 + // map是否包含7这个值, map.get(complement)就是7的索引 if (map.containsKey(complement)){ //所以只判断是否包含该元素和上面代码是一样的 return new int[]{i, map.get(complement)}; } - map.put(nums[i], i); + // 每次都将差值和索引存入, 以后如果数组存在差值这个元素, 那就直接取出value索引就找到了 + map.put(nums[i], i);// map 可以为值, value为索引 } return new int[0]; } diff --git a/src/main/java/com/study/search/BinarySearch.java b/src/main/java/com/study/search/BinarySearch.java index c224e8b..a779a7e 100644 --- a/src/main/java/com/study/search/BinarySearch.java +++ b/src/main/java/com/study/search/BinarySearch.java @@ -14,8 +14,7 @@ public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7, 8}; //int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}; - //System.out.println(search(arr, 1)); - System.out.println(search2(arr, 3)); + System.out.println(search(arr, 1)); } /** @@ -34,7 +33,7 @@ private static int search(int[] arr, int key) { while (low <= high) { // 先从中间开始查找 //int mid = (low + high) / 2; // low + high 可能会大于 Integer.MAX_VALUE - int mid = (high - low) >> 1 + low; + int mid = ((high - low) >> 1) + low; // 如果小于中间的,就把范围改成最左边到中间-1 if (key < arr[mid]) { high = mid - 1; @@ -49,34 +48,4 @@ private static int search(int[] arr, int key) { //没找到返回-1 return -1; } - - - /** - * 折半查找,每次查找都基于中间值来对比 来缩小查找范围, 时间复杂度为O(logN) - * - * @param arr - * @param element - * @return - */ - private static int search2(int[] arr, int element) { - int low = 0, high = arr.length - 1; - - // 循环条件是最低位索引小于等于最高位索引 - while (low <= high) { - //mid = (low + high) / 2; // low + high 可能会大于 Integer.MAX_VALUE - int mid = (high - low) >> 1 + low; - // 如果元素大于中间值, 则下次从右边查找 - if (arr[mid] < element) { - // 将最小索引改为中间+! - low = mid + 1; - } else if (arr[mid] > element) { - // 将最大索引改为中间-1 - high = mid - 1; - } else { - return mid; - } - } - - return -1; - } } diff --git a/src/main/java/com/study/search/BinarySearch2.java b/src/main/java/com/study/search/BinarySearch2.java index bb0fb03..68a990f 100644 --- a/src/main/java/com/study/search/BinarySearch2.java +++ b/src/main/java/com/study/search/BinarySearch2.java @@ -2,12 +2,11 @@ /** * 二分查找4种变形题: - * + *

* 找到第一个相同的元素(存在重复的元素) * 找到最后一个相同的元素(存在重复的元素) * 找到第一个大于等于的元素(存在重复的元素) * 找到最后一个小于等于的元素(存在重复的元素) - * */ public class BinarySearch2 { @@ -27,8 +26,9 @@ public static void main(String[] args) { System.out.println(find(arr, 4)); System.out.println(find(arr, 5)); System.out.println("---------------查找第一个相同元素"); - arr = new int[]{-2, -1, 0, 2, 2, 3, 3, 5}; + //arr = new int[]{-2, -1, 0, 2, 2, 3, 3, 5}; //arr = new int[]{}; + arr = new int[]{-2}; System.out.println(findFirst(arr, -2)); System.out.println(findFirst(arr, 0)); System.out.println(findFirst(arr, 2)); @@ -42,30 +42,29 @@ public static void main(String[] args) { System.out.println(findLast(arr, 5)); System.out.println("---------------查找第一个大于等于元素"); arr = new int[]{-2, 0, 2, 2, 3, 3, 5}; - System.out.println(findFirstLargerEquals4(arr, -2)); //0 - System.out.println(findFirstLargerEquals4(arr, -1)); //1 - System.out.println(findFirstLargerEquals4(arr, 1)); //2 - System.out.println(findFirstLargerEquals4(arr, 5)); //6 - System.out.println(findFirstLargerEquals4(arr, 2)); //2 - System.out.println(findFirstLargerEquals4(arr, 7)); //-1 没找到 - System.out.println(findFirstLargerEquals4(arr, 0)); //1 + System.out.println(findFirstLargerEquals(arr, -2)); //0 + System.out.println(findFirstLargerEquals(arr, -1)); //1 + System.out.println(findFirstLargerEquals(arr, 1)); //2 + System.out.println(findFirstLargerEquals(arr, 5)); //6 + System.out.println(findFirstLargerEquals(arr, 2)); //2 + System.out.println(findFirstLargerEquals(arr, 7)); //-1 没找到 + System.out.println(findFirstLargerEquals(arr, 0)); //1 arr = new int[]{-2}; - System.out.println(findFirstLargerEquals4(arr, -3)); //0 + System.out.println(findFirstLargerEquals(arr, -3)); //0 arr = new int[]{}; - System.out.println(findFirstLargerEquals4(arr, -3)); //-1 + System.out.println(findFirstLargerEquals(arr, -3)); //-1 System.out.println("---------------查找最后一个小于等于元素"); arr = new int[]{-2, 0, 2, 2, 3, 3, 5}; - System.out.println(findLastSmallerEquals2(arr, -2)); //0 - System.out.println(findLastSmallerEquals2(arr, -1)); //0 - System.out.println(findLastSmallerEquals2(arr, 1)); //1 - System.out.println(findLastSmallerEquals2(arr, 5)); //6 - System.out.println(findLastSmallerEquals2(arr, 2)); //3 - System.out.println(findLastSmallerEquals2(arr, 7)); //6 - System.out.println(findLastSmallerEquals2(arr, 0)); //1 - + System.out.println(findLastSmallerEquals(arr, -2)); //0 + System.out.println(findLastSmallerEquals(arr, -1)); //0 + System.out.println(findLastSmallerEquals(arr, 1)); //1 + System.out.println(findLastSmallerEquals(arr, 5)); //6 + System.out.println(findLastSmallerEquals(arr, 2)); //3 + System.out.println(findLastSmallerEquals(arr, 7)); //6 + System.out.println(findLastSmallerEquals(arr, 0)); //1 // System.out.println(findLastLargerEquals(arr, 3)); // System.out.println(findLastLargerEquals(arr, 4)); } @@ -101,7 +100,7 @@ private static int find(int[] arr, int target) { /** * 找到第一个相同的元素(存在重复的元素) - * + *

* 如果找到, 再往前找, 找到的条件是前一个小于目标元素, 那么当前这个就是目标元素. (往前找需要考虑边界值为0,已经找到第一个了还等于目标,那直接返回) * * @param array @@ -132,33 +131,9 @@ public static int findFirst(int[] array, int target) { return -1; } - - //3.变式:查找最后一个值等于给定值的元素( 存在重复的数据) - public int binarySearch5(int[] array, int target) { - int left = 0, mid = 0, right = array.length - 1; - - while (left <= right) { - mid = (right - left) >> 1 + left; - - if (array[mid] < target) { - left = mid + 1; - } else if (array[mid] > target) { - right = mid - 1; - } else { //相等的前提下 - if (mid == array.length - 1 || (array[mid + 1] != target)) { - return mid; - } else { - left = mid + 1; //往后找 - } - } - } - - return -1; - } - /** * 找到最后一个相同的元素, - * + *

* 找到的基础上往后找, 如果后一个大于目标元素,则当前为最后一个相同元素. 边界值为当前元素为最后一个 * * @return @@ -184,65 +159,6 @@ private static int findLast(int[] arr, int target) { return -1; } -// /** -// * 查找第一个大于等于给定值的元素( 存在重复的数据) -// * -// * @param array -// * @param target -// * @return -// */ -// public static int binarySearch4(int[] array, int target) { -// int left = 0, right = array.length - 1; -// -// while (left <= right) { -// int mid = ((right - left) >> 1) + left; -// -// if (array[mid] >= target) { -// if (mid == array.length - 1 || (array[mid + 1] != target)) { -// return mid; -// } else { -// right = mid - 1; -// } -// } else { -// left = mid + 1; //往后找 -// } -// } -// -// return -1; -// } - - /** - * 查找第一个大于等于给定值的元素(存在重复的数据) - * - * @param arr - * @param target - * @return - */ - public static int findFirstLargerEquals(int[] arr, int target) { - int left = 0; - int right = arr.length - 1; - - while (left <= right) { - int mid = ((right - left) >> 1) + left; - // 小于目标, 往后找 - if (arr[mid] < target) { - left = mid + 1; - } else { - // 大于或者等于目标, 需要判断是否已经找到, 如果没找到再往前找 - // 如果已经处于第0个元素 或者 前一个元素小于目标, 说明已经找到 - if (mid == 0 || arr[mid - 1] < target) { - return mid; - } else { - // 否则 继续往前找, 将越来越接近第一个大于等于的元素 - right = mid - 1; - } - } - } - - // 找不到 返回-1 - return -1; - } - /** * 找到 第一个大于等于目标元素的位置 *

@@ -254,7 +170,7 @@ public static int findFirstLargerEquals(int[] arr, int target) { * @param target * @return */ - public static int findFirstLargerEquals3(int[] arr, int target) { + public static int findFirstLargerEquals(int[] arr, int target) { int left = 0; int right = arr.length - 1; @@ -278,63 +194,6 @@ public static int findFirstLargerEquals3(int[] arr, int target) { return -1; } - public static int findFirstLargerEquals4(int[] arr, int target) { - int left = 0; - int right = arr.length - 1; - - while (left <= right) { - int mid = ((right - left) >> 1) + left; - // 大于等于 往前找 - if (arr[mid] >= target) { - // 大于或者等于的情况, 因为大于等于的目标元素非常多,所以需要判断是否为第一个 - // 需要考虑一些边界值, 如果已经找到了第一个元素 还比目标大, 那就直接返回这个元素 - // 或者前一个元素小于目标, 那当前元素肯定是第一个大于等于的元素 - if (mid == 0 || arr[mid - 1] < target) { - return mid; - } else { - // 如果没找到目标, 就往前找, 最终一定能找到第一个大于等于的 - right = mid - 1; - } - } else { - // 如果小于,完全不满足条件,往后找 - left = mid + 1; - } - } - return -1; - } - - /** - * 查找最后一个小于等于给定值的元素( 存在重复的数据) - *

- * 思路: 如果大于就往前找, 如果小于等于 就往后找(因为小于等于目标元素的特别多), 直到找到最后一个小于等于目标元素的 - *

- * https://blog.csdn.net/hyforthy/article/details/22614247 - * - * @param array - * @param target - * @return - */ - private static int findLastSmallerEquals(int[] array, int target) { - int left = 0, right = array.length - 1; - - while (left <= right) { - int mid = ((right - left) >> 1) + left; - // 如果大于就往前找 - if (array[mid] > target) { - right = mid - 1; - } else { - // 如果小于等于 就往后找, 直到找到目标元素 - // 下一个元素大于目标, 那当前元素为最后一个小于等于目标的 - // 需要考虑一些边界值, 如果已经找到了最后一个元素 还比目标小, 那就直接返回这个元素 - if (mid == array.length - 1 || array[mid + 1] > target) - return mid; - else // 否则继续往后找 - left = mid + 1; - } - } - return -1; - } - /** * 找最后一个小于等于目标元素的索引(目标元素之前的最后一个) *

@@ -345,7 +204,7 @@ private static int findLastSmallerEquals(int[] array, int target) { * @param target * @return */ - private static int findLastSmallerEquals2(int[] array, int target) { + private static int findLastSmallerEquals(int[] array, int target) { int left = 0; int right = array.length - 1; @@ -365,25 +224,5 @@ private static int findLastSmallerEquals2(int[] array, int target) { // 没找到 return -1; } - - //5.变式:查找最后一个小于等于给定值的元素( 存在重复的数据) -// public static int binarySearch6(int[] array, int target) { -// int left = 0, mid = 0, right = array.length - 1; -// -// while (left <= right) { -// mid = (right - left) >> 1 + left; -// -// if (array[mid] <= target) { -// if (mid == array.length - 1 || (array[mid + 1] != target)) { -// return mid; -// } else { -// left = mid + 1; -// } -// } else {//当前值比 参考值 大, 往前找 -// right = mid - 1; -// } -// } -// -// return -1; -// } } + diff --git a/src/main/java/com/study/search/FindSmall.java b/src/main/java/com/study/search/FindSmall.java index e834615..e53afee 100644 --- a/src/main/java/com/study/search/FindSmall.java +++ b/src/main/java/com/study/search/FindSmall.java @@ -8,14 +8,14 @@ * 比如有序数组为 [2, 3, 6, 7, 8, 10, 12, 15], 将后面的10,12,15截取后放到数组的最前面,变成[10, 12, 15, 2, 3, 6, 7, 8] *

* 问题一: 请写一个算法, 找出最小值 + * https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/ *

* 问题二: 给定一个数,查找其在序列中是否存在(返回其位置),请问如何实现? + * https://leetcode-cn.com/problems/search-in-rotated-sorted-array/ *

* 问题三: 给定任意一个序列,找出其中的一个谷/峰,谷的定义为两边的数均大于某个数。 *

* - * https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/ - * * https://segmentfault.com/q/1010000000181714 *

* https://blog.csdn.net/weixin_34018169/article/details/89698260?utm_source=distribute.pc_relevant.none-task diff --git a/src/main/java/com/study/search/MinAbs.java b/src/main/java/com/study/search/MinAbs.java deleted file mode 100644 index 356449d..0000000 --- a/src/main/java/com/study/search/MinAbs.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.study.search; - -/** - * 找出有序数组中绝对值最小的元素 - */ -public class MinAbs { - - public static void main(String[] args) { - // 一个升序整形数组, 找到绝对值最小的输出 - - //int[] arr = {-3,-2,-1,5}; - //int[] arr = {-3, -2, -1, 0, 3, 5}; - //int[] arr = {-1, 1, 3, 5}; - - //int[] arr = {1, 3, 5}; - //int[] arr = {-9, -6, - 2, 0}; - //int[] arr = {-6}; - int[] arr = {}; - System.out.println(min(arr)); - } - - /** - * 利用二分查找进行,使得时间复杂度为O(logN), 低于遍历的O(N) - * - * @param arr - * @return - */ - private static int min(int[] arr) { - int left = 0; - int right = arr.length - 1; - - while (left <= right) { - //int middle = (left + right) / 2; // 这段代码可能越界 - int middle = left + (right - left) >>1; //防止越界 - // 大于0往左找最近的负数 - if (arr[middle] > 0) { - // 判断下一个数是否为负数 - if (middle - 1 >= 0 && arr[middle - 1] < 0) { - // 返回正负交届的绝对值最小的那个数 - if (Math.abs(arr[middle]) > Math.abs(arr[middle - 1])) - return arr[middle - 1]; - else - return arr[middle]; - } else - right = middle - 1; //从左边找, 右边界需要减一,因为有边界这个元素不满足 - // 小于0往右找最近的正数 - } else if (arr[middle] < 0) { - // 判断下一个数是否为正数 (注意边界) - if (middle + 1 < arr.length && arr[middle + 1] > 0) - // 返回正负交届的绝对值最小的那个数 - if (Math.abs(arr[middle]) > Math.abs(arr[middle + 1])) - return arr[middle + 1]; - else - return arr[middle]; - else - left = middle + 1; // 从左边找 左边界需要+1,因为边界这个元素不满足 - } else { - // 如果为0,直接返回 (0是所有数中绝对值最小的) - return arr[middle]; - } - } - return -1; - } -} diff --git a/src/main/java/com/study/sort/QuickSort.java b/src/main/java/com/study/sort/QuickSort.java index d0e86a3..54b87dc 100644 --- a/src/main/java/com/study/sort/QuickSort.java +++ b/src/main/java/com/study/sort/QuickSort.java @@ -18,16 +18,22 @@ */ public class QuickSort { + /** + * 快速排序算法有很多: 挖坑法, 左右指针法, 前后指针法 + * + * @param args + */ public static void main(String[] args) { //int[] a = {12, 20, 5, 16, 15, 1, 30, 45}; - int[] a = {21, 32, 43, 98, 54, 45, 23, 4, 66, 86}; + //int[] a = {21, 32, 43, 98, 54, 45, 23, 4, 66, 86}; + int[] a = {72, 6, 57, 88, 60, 42, 83, 73, 48, 85}; // int[] a = {12, 20, 9, 5, 16}; //int[] a = {12, 1}; print(a); int start = 0; int end = a.length - 1; - sort(a, start, end); - //sort2(a, start, end); + //sort(a, start, end); + sort2(a, start, end); for (int i = 0; i < a.length; i++) { System.out.printf("%s ", a[i]); } @@ -45,7 +51,7 @@ public static void main(String[] args) { *

* 反复循环,直到start和end重叠, 退出循环. *

- * 此时,左边的值都比基准值小,右边的值都比基准值大,但是两边的顺序还有可能是不一样的. + * 此时, start左边的值都小于或等于基准值,右边的都大于或等于基准值,但是两边的顺序还有可能是不一样的. *

* 所以还需要进行递归调整一下, 递归分别从基准值左右两边进行调整 */ @@ -54,7 +60,7 @@ private static void sort(int[] a, int low, int high) { int end = high; // 选取第一个元素作为基准值 int key = a[low]; - System.out.println("基准值为: "+ key); + System.out.println("基准值为: " + key); // 循环里 start往后挪, end往前挪 while (end > start) { // 从后往前比较 @@ -84,18 +90,19 @@ private static void sort(int[] a, int low, int high) { print(a); } } - // 当循环结束后,基准值的位置已经确定了。左边的值都比基准值小,右边的值都比基准值大,但是两边的顺序还有可能是不一样的. + // 当循环结束后,基准值的位置已经确定了。start左边的值都小于或等于基准值,end右边的值都大于或等于基准值,但是两边的顺序还有可能是不一样的. - // 于是将start和end左右两边划分成两个区间, 分别进行递归排序, 等左右两个区间都完成排序后,那么整个数组就变成有序了 + // 于是将start和end左右两边划分成两个区间, 不停的进行二分分区, 最后等左右两个区间都完成排序后,那么整个数组就变成有序了 + // 不管是左边还是右边区间, 都取第0个作为基准值 // 将左边序列进行排序 if (start > low) { - sort(a, low, start - 1); + sort(a, low, start - 1); // 取左边数组第0个做基准值, 最小索引不变, 然后最大范围不断缩小 System.out.println("start: " + start); } // 将右边序列进行排序 if (end < high) { - sort(a, end + 1, high); + sort(a, end + 1, high); // 取右边数组第0个作基准值, 最大索引不变, 最小范围不断加大 System.out.println("end: " + end); } } @@ -104,46 +111,38 @@ private static void sort2(int[] arr, int low, int high) { int start = low; int end = high; - // 取第0个元素为基准值 int key = arr[low]; while (start < end) { - - // 后面的比基准值大的 - while (start < end && arr[end] >= key) { - end--; //往前挪 + // end从后往前和基准值做比较, 小于基准值就和start进行交换 + while (end > start && arr[end] >= key) { + end--; } - // 直到遇上比基准值小的, 前后替换 + //如果后面存在元素小于基准值, 将将start和end位置上的元素互换 if (arr[end] < key) { int tmp = arr[end]; arr[end] = arr[start]; arr[start] = tmp; - System.out.println("后面比基准值小"); - print(arr); } - // 前面比基准值小的 - while (start < end && arr[start] <= key) { - start++; //往后挪 + while (end > start && arr[start] <= key) { + start++; } + //如果前面有元素大于基准值, 就将start和end位置上的元素互换 if (arr[start] > key) { int tmp = arr[start]; arr[start] = arr[end]; arr[end] = tmp; - System.out.println("前面比基准值大"); - print(arr); } } if (start > low) { - System.out.println("low" + low); sort2(arr, low, start - 1); } if (end < high) { - System.out.println("high" + high); sort2(arr, end + 1, high); } } diff --git a/src/main/java/com/study/sort/SelectSort.java b/src/main/java/com/study/sort/SelectSort.java index 071b5e2..3d94ce5 100644 --- a/src/main/java/com/study/sort/SelectSort.java +++ b/src/main/java/com/study/sort/SelectSort.java @@ -18,20 +18,22 @@ public static void main(String[] args) { int[] a = {12, 23, 9, 24, 15, 3, 18}; - selectSort(a); + //selectSort(a); + selectSort2(a); print(a); System.out.println(); - selectSortDesc(a); + //selectSortDesc(a); + selectSortDesc2(a); print(a); } /** * 选择排序, 从每次遍历未排序的元素中找出值最小的元素索引 - * + *

* 每一次循环能找出一个当前最小的索引,然后将最小索引上的元素放在当前遍历的索引上 - * + *

* 跟冒泡排序类似,只是它不会做元素的两两替换,而是找最大或最小索引,找到后再替换 */ private static void selectSort(int[] a) { @@ -54,9 +56,27 @@ private static void selectSort(int[] a) { } } + private static void selectSort2(int[] arr) { + for (int i = 0; i < arr.length; i++) { + // 每一轮循环找出一个最小值索引 + int min = i; + for (int j = i + 1; j < arr.length; j++) { + if (arr[j] < arr[min]) { + min = j; + } + } + + // 最小索引不再是当前元素的索引, 那就需要将最小元素和当前元素位置替换一下 + if (min != i) { + int tmp = arr[min]; + arr[min] = arr[i]; + arr[i] = tmp; + } + } + } + /** * 从大到小排序 - * */ private static void selectSortDesc(int[] a) { for (int i = 0; i < a.length - 1; i++) { @@ -76,6 +96,24 @@ private static void selectSortDesc(int[] a) { } } + + private static void selectSortDesc2(int[] arr) { + for (int i = 0; i < arr.length - 1; i++) {// 这里用arr.length 或者arr.length -1都行, 因为排完前面的, 最后一个肯定是最小的 + int maxIndex = i; + for (int j = i + 1; j < arr.length; j++) { + if (arr[j] > arr[maxIndex]) { + maxIndex = j; + } + } + + if (i != maxIndex) { + int tmp = arr[maxIndex]; + arr[maxIndex] = arr[i]; + arr[i] = tmp; + } + } + } + private static void print(int[] arr) { for (int i = 0; i < arr.length; i++) { if (i == arr.length - 1) { diff --git a/src/main/java/com/study/string/FirstUniqueCharacter.java b/src/main/java/com/study/string/FirstUniqueCharacter.java new file mode 100644 index 0000000..2ecbda7 --- /dev/null +++ b/src/main/java/com/study/string/FirstUniqueCharacter.java @@ -0,0 +1,35 @@ +package com.study.string; + + +/** + * 字符串中的第一个唯一字符 + * + * 给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。 + * + * 案例: + * + * s = "leetcode" + * 返回 0. + * + * s = "loveleetcode", + * 返回 2. + *   + * + * 注意事项:您可以假定该字符串只包含小写字母。 + * + * 链接:https://leetcode-cn.com/problems/first-unique-character-in-a-string + * + */ +public class FirstUniqueCharacter { + + /** + * 方法一: 遍历字符串使用hashmap来存每个字符的个数, 遍历hashmap,找到第一个不重复的字符 + * 方法二: 使用char[256]数组代替hashmap + * + * @param args + */ + public static void main(String[] args) { + String s = "leetcode"; + + } +} diff --git a/src/main/java/com/study/string/GenerateParentheses.java b/src/main/java/com/study/string/GenerateParentheses.java index 49b3915..b05db23 100644 --- a/src/main/java/com/study/string/GenerateParentheses.java +++ b/src/main/java/com/study/string/GenerateParentheses.java @@ -31,8 +31,8 @@ public class GenerateParentheses { */ public static void main(String[] args) { //List resultList = generateParenthesis(3); - List resultList = generateParenthesis2(3); - + //List resultList = generateParenthesis2(3); + List resultList = generateParenthesis(3); for (String result : resultList) { System.out.println(result); } @@ -42,23 +42,23 @@ private static List generateParenthesis(int n) { List resultList = new ArrayList<>(); //gen(0, 0, n, "", resultList); //gen2(0, 0, n, "", resultList); - gen3(0, 0, n, "", resultList); - + //gen3(0, 0, n, "", resultList); + gen2_2(0, 0, n, "", resultList); return resultList; } /** * 闭合数 * 思路 - * + *

* 为了枚举某些内容,我们通常希望将其表示为更容易计算的不相交子集的总和。 - * + *

* 考虑有效括号序列 S 的 闭包数:至少存在 index >= 0,使得 S[0], S[1], ..., S[2*index+1]是有效的。 显然,每个括号序列都有一个唯一的闭包号。 我们可以尝试单独列举它们。 - * + *

* 算法 - * + *

* 对于每个闭合数 c,我们知道起始和结束括号必定位于索引 0 和 2*c + 1。然后两者间的 2*c 个元素一定是有效序列,其余元素一定是有效序列。 - * + *

* https://leetcode-cn.com/problems/generate-parentheses/solution/gua-hao-sheng-cheng-by-leetcode/ * * @param n @@ -70,8 +70,8 @@ private static List generateParenthesis2(int n) { ans.add(""); } else { for (int c = 0; c < n; ++c) - for (String left: generateParenthesis2(c)) - for (String right: generateParenthesis2(n-1-c)) + for (String left : generateParenthesis2(c)) + for (String right : generateParenthesis2(n - 1 - c)) ans.add("(" + left + ")" + right); } return ans; @@ -132,6 +132,24 @@ private static void gen2(int left, int right, int n, String result, List } } + private static void gen2_2(int left, int right, int n, String result, List resultList) { + // 当左右空格数量都达到3个的时候, 一次括号组合完成 + if (left == n && right == n) { + resultList.add(result); + return; + } + + // 一定要先放左括号 + if (left < n) { + gen2_2(left + 1, right, n, result + "(", resultList); + } + + // 右边括号数量不超过嘴边的情况下可以放入 + if (right < left) { + gen2_2(left, right + 1, n, result + ")", resultList); + } + } + /** * 递归中使用剪枝 或者也叫回溯 * @@ -150,7 +168,7 @@ private static void gen3(int left, int right, int n, String result, List // 使用剪枝的方式 去除右括号大于左括号的所有情况 // 剪枝 也是回溯法的一种解题思路, 因为当前做法不满足条件,所以直接返回避免无效操作 - if(left < right) + if (left < right) return; // 先有左括号 再有右括号, 而且右括号一定不能大于左括号数量 diff --git a/src/main/java/com/study/string/JewelsAndStones.java b/src/main/java/com/study/string/JewelsAndStones.java index efed918..595a61b 100644 --- a/src/main/java/com/study/string/JewelsAndStones.java +++ b/src/main/java/com/study/string/JewelsAndStones.java @@ -30,20 +30,20 @@ public class JewelsAndStones { public static void main(String[] args) { - //String j = "aA", s = "aAAbbbb"; + String j = "aA", s = "aAAbbbb"; //String j = "z", s = "ZZ"; //System.out.println(numJewelsInStones(j,s)); - String j = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", s = ""; - for (int i = 0; i < SIZE; i++) { - if (i == SIZE - 2) { - s += 'Y'; - } else if (i == SIZE - 1) { - s += 'Z'; - } else { - s += i; - } - } +// String j = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", s = ""; +// for (int i = 0; i < SIZE; i++) { +// if (i == SIZE - 2) { +// s += 'Y'; +// } else if (i == SIZE - 1) { +// s += 'Z'; +// } else { +// s += i; +// } +// } long start = System.currentTimeMillis(); diff --git a/src/main/java/com/study/string/LongestNumberInString.java b/src/main/java/com/study/string/LongestNumberInString.java index d4c31d7..943dded 100644 --- a/src/main/java/com/study/string/LongestNumberInString.java +++ b/src/main/java/com/study/string/LongestNumberInString.java @@ -8,7 +8,8 @@ public static void main(String[] args) { String input = "abcd123cc45678977ed125ss6789"; //String longestNum = getLongestNumber(input); - String longestNum = getLongestNumber2(input); + //String longestNum = getLongestNumber2(input); + String longestNum = getLongestNumber3(input); System.out.printf("最长连续数字为%s,长度为%d", longestNum, longestNum.length()); } @@ -65,4 +66,24 @@ private static String getLongestNumber2(String input) { } return longestNum; } + + + private static String getLongestNumber3(String input) { + String longestNum = ""; + String currentNum = ""; + + for(int i =0; i < input.length(); i++){ + // 位于0-9之间 + if(input.charAt(i) >= '0' && input.charAt(i) <= '9'){ + currentNum += input.charAt(i); + }else{ + if(currentNum.length() > longestNum.length()){ + longestNum = currentNum; + } + currentNum = ""; + } + } + + return longestNum; + } } diff --git a/src/main/java/com/study/string/LongestPalindrome.java b/src/main/java/com/study/string/LongestPalindrome.java index a8beee1..17bae22 100644 --- a/src/main/java/com/study/string/LongestPalindrome.java +++ b/src/main/java/com/study/string/LongestPalindrome.java @@ -48,9 +48,10 @@ public static void main(String[] args) { System.out.println("--------------------"); //System.out.println(longestPalindrome(s)); - System.out.println(longestPalindrome_2(s)); + //System.out.println(longestPalindrome_2(s)); //System.out.println(longestPalindrome2(s)); //System.out.println(longestPalindrome3(s)); + System.out.println(longestPalindrome_3(s)); } @@ -118,6 +119,27 @@ public static int longestPalindrome_2(String s) { return length; } + public static int longestPalindrome_3(String s) { + char[] chars = new char[128]; + + for (int i = 0; i < s.length(); i++) { + chars[s.charAt(i)]++; + } + + int length = 0; + // 记录可以做成回文数的字符个数 + for (int c : chars) { + length += c / 2 * 2; // 如果是偶数得到本身, 如果是奇数则-1, 如果是0得到0 + + if (length % 2 == 0 && c % 2 == 1) { //如果当前总数为偶数,而且c本身的个数为奇数, 可以使用这个c + length++; + } + } + + return length; + } + + /** * 贪心算法, 使用hashmap来存储每个字符 * diff --git a/src/main/java/com/study/string/ReverseWordsInAString.java b/src/main/java/com/study/string/ReverseWordsInAString.java index a2e8f55..5e0ad6b 100644 --- a/src/main/java/com/study/string/ReverseWordsInAString.java +++ b/src/main/java/com/study/string/ReverseWordsInAString.java @@ -34,9 +34,6 @@ */ public class ReverseWordsInAString { - /** - * @param args - */ public static void main(String[] args) { //String s = "the sky is blue"; String s = "hello world!"; diff --git a/src/main/java/com/study/string/ToUpperCase.java b/src/main/java/com/study/string/ToUpperCase.java index aa72c44..6459f53 100644 --- a/src/main/java/com/study/string/ToUpperCase.java +++ b/src/main/java/com/study/string/ToUpperCase.java @@ -17,6 +17,8 @@ public static void main(String[] args) { * * 利用ascii大小写字母对应的差值进行转换 * + * 小于字母是 97 - 122 大写字母是 65 - 90 比大些字母多了32 + * * @param str * @return */ diff --git a/src/main/java/com/study/string/UniqueEmailAddresses.java b/src/main/java/com/study/string/UniqueEmailAddresses.java index 8525bbe..9464925 100644 --- a/src/main/java/com/study/string/UniqueEmailAddresses.java +++ b/src/main/java/com/study/string/UniqueEmailAddresses.java @@ -39,7 +39,7 @@ public static void main(String[] args) { * * 这里使用了hashset,它的add()的时间复杂度为O(1), 并且不会存在相同的元素 * - * 时间复杂度O(1) + * 时间复杂度O(n) * @param emails * @return */ diff --git a/src/main/java/com/study/tree/binarytree/BFS.java b/src/main/java/com/study/tree/binarytree/BFS.java index d0e0589..1975f27 100644 --- a/src/main/java/com/study/tree/binarytree/BFS.java +++ b/src/main/java/com/study/tree/binarytree/BFS.java @@ -1,6 +1,6 @@ package com.study.tree.binarytree; -import com.study.binarytree.TreeNode; +import com.study.tree.binarytree.TreeNode; import com.study.utils.TreeUtils; import java.util.*; diff --git a/src/main/java/com/study/tree/binarytree/BalancedBinaryTree.java b/src/main/java/com/study/tree/binarytree/BalancedBinaryTree.java index 65bb57b..80a31f7 100644 --- a/src/main/java/com/study/tree/binarytree/BalancedBinaryTree.java +++ b/src/main/java/com/study/tree/binarytree/BalancedBinaryTree.java @@ -126,17 +126,17 @@ private static int depth(TreeNode root) { // 左子树出现不满足条件的 int left = depth(root.left); - if (left == -1) + if (left == -1) // 得到-1 说明不满足条件 return -1; // 右子树出现不满足条件的 int right = depth(root.right); - if (right == -1) + if (right == -1) // 得到-1 说明不满足条件 return -1; - // 检查是否满足条件 + // 检查是否满足条件, 左右子树高度差绝对值不超过1 if (Math.abs(left - right) > 1) - return -1; + return -1; //不满足条件就返回-1 // 返回当前左右子树的最大高都 return Math.max(left, right) + 1; diff --git a/src/main/java/com/study/tree/binarytree/DFS.java b/src/main/java/com/study/tree/binarytree/DFS.java index 36452e7..8844dff 100644 --- a/src/main/java/com/study/tree/binarytree/DFS.java +++ b/src/main/java/com/study/tree/binarytree/DFS.java @@ -1,6 +1,6 @@ package com.study.tree.binarytree; -import com.study.binarytree.TreeNode; +import com.study.tree.binarytree.TreeNode; import com.study.utils.TreeUtils; import java.util.ArrayList; diff --git a/src/main/java/com/study/tree/binarytree/LevelTraversal2.java b/src/main/java/com/study/tree/binarytree/LevelTraversal2.java index bdadd06..96802b3 100644 --- a/src/main/java/com/study/tree/binarytree/LevelTraversal2.java +++ b/src/main/java/com/study/tree/binarytree/LevelTraversal2.java @@ -2,7 +2,6 @@ import com.study.utils.TreeUtils; -import sun.reflect.generics.tree.Tree; import java.util.ArrayList; import java.util.LinkedList; diff --git a/src/main/java/com/study/tree/ntree/NAryTreePostorderTraversal.java b/src/main/java/com/study/tree/ntree/NAryTreePostorderTraversal.java new file mode 100644 index 0000000..fc02bbc --- /dev/null +++ b/src/main/java/com/study/tree/ntree/NAryTreePostorderTraversal.java @@ -0,0 +1,35 @@ +package com.study.tree.ntree; + +import java.util.ArrayList; +import java.util.List; + +/** + * N叉树的后序遍历 + *

+ * 给定一个 N 叉树,返回其节点值的后序遍历。 + *

+ * https://leetcode-cn.com/problems/n-ary-tree-postorder-traversal/ + */ +public class NAryTreePostorderTraversal { + + /** + * 可以使用 递归 或者 迭代+2个stack的方式 来处理 + * + * @param args + */ + public static void main(String[] args) { + + } + + private List list = new ArrayList(); + + public List postOrder(NTreeNode root) { + if (root != null) { + for (NTreeNode node : root.children) { + postOrder(node); + } + list.add(root.val); + } + return list; + } +} diff --git a/src/main/java/com/study/utils/TreeUtils.java b/src/main/java/com/study/utils/TreeUtils.java index f36ebf6..6149d98 100644 --- a/src/main/java/com/study/utils/TreeUtils.java +++ b/src/main/java/com/study/utils/TreeUtils.java @@ -1,7 +1,7 @@ package com.study.utils; -import com.study.binarytree.TreeNode; -import com.study.binarytree.TreeNode2; +import com.study.tree.binarytree.TreeNode; +import com.study.tree.binarytree.TreeNode2; import java.util.ArrayList; import java.util.List; From 6350b17d940fd64987d52836b3a2109c9a9a8e67 Mon Sep 17 00:00:00 2001 From: longwu Date: Wed, 18 Mar 2020 23:33:40 +0800 Subject: [PATCH 74/74] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/study/array/SingleNumber.java | 153 ++++++++++++++++++ .../java/com/study/search/FindMinAbs.java | 64 ++++++++ .../com/study/search/SearchA2dMatrix.java | 38 +++++ src/main/java/com/study/sort/HeapSort.java | 15 ++ 4 files changed, 270 insertions(+) create mode 100644 src/main/java/com/study/array/SingleNumber.java create mode 100644 src/main/java/com/study/search/FindMinAbs.java create mode 100644 src/main/java/com/study/search/SearchA2dMatrix.java create mode 100644 src/main/java/com/study/sort/HeapSort.java diff --git a/src/main/java/com/study/array/SingleNumber.java b/src/main/java/com/study/array/SingleNumber.java new file mode 100644 index 0000000..5027d98 --- /dev/null +++ b/src/main/java/com/study/array/SingleNumber.java @@ -0,0 +1,153 @@ +package com.study.array; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * 只出现一次的数字 + *

+ * 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 + *

+ * 说明: + *

+ * 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗? + *

+ * 示例 1: + *

+ * 输入: [2,2,1] + * 输出: 1 + * 示例 2: + *

+ * 输入: [4,1,2,1,2] + * 输出: 4 + *

+ * 链接:https://leetcode-cn.com/problems/single-number + */ +public class SingleNumber { + + public static void main(String[] args) { + //int[] arr = {2, 2, 1}; + int[] arr = {4, 1, 2, 1, 2}; + //System.out.println(singleNumberHashSet(arr)); + //System.out.println(singleNumberHashMap(arr)); + //System.out.println(singleNumberBySum(arr)); + System.out.println(singleNumberByBit(arr)); + } + + /** + * 使用hashset来存每个元素, 当元素出现2次的时候,移除该元素,所以只会保留出现一次的元素. + * 最终hashset里面只会存1个元素, 即为结果 + *

+ * 时间复杂度 O(n), 空间复杂度O(n) + * + * @param nums + * @return + */ + private static int singleNumberHashSet(int[] nums) { + if (nums.length == 1) + return nums[0]; + + Set set = new HashSet(); + for (int i = 0; i < nums.length; i++) { + // 如果该元素已经存在, 就移除 + if (set.contains(nums[i])) + set.remove(nums[i]); + else // 不存在 就添加 + set.add(nums[i]); + } + + // 最终set里面只有一个元素即为结果 + for (Integer i : set) { + return i; + } + + return -1; + } + + /** + * 使用hashmap来存每个数字的个数, 最后遍历hashmap,找出个数为1的那个数字 + *

+ * 时间复杂度 O(n), 空间复杂度O(n) + * + * @param nums + * @return + */ + private static int singleNumberHashMap(int[] nums) { + if (nums.length == 1) + return nums[0]; + + Map map = new HashMap(); + for (int i = 0; i < nums.length; i++) { + // 如果该元素已经存在, 就移除 + if (map.containsKey(nums[i])) + map.put(nums[i], map.get(nums[i]) + 1); + else // 不存在 就添加 + map.put(nums[i], 1); + } + + // 最终set里面只有一个元素即为结果 + for (Map.Entry kv : map.entrySet()) { + if (kv.getValue() == 1) + return kv.getKey(); + } + + return -1; + } + + + /** + * 通过数学公式 2倍的不重复的元素之和 - 数组元素之和 = 单个元素 + * 2(a+b+c) - (2a + 2b + c) = c + *

+ * 遍历数组, 计算数组所有元素值 + * 使用set来存储不重复的元素, 存储的同时 计算不重复元素值 + * 使用不重复元素之和的2倍 - 数组元素值之和 = 单个元素 + *

+ * 时间复杂度 O(n), 空间复杂度O(n) + * + * @param nums + * @return + */ + private static int singleNumberBySum(int[] nums) { + Set set = new HashSet(); + + int twoSum = 0; + int arrSum = 0; + + for (int n : nums) { + arrSum += n; // 计算数组元素之和 + if (!set.contains(n)) { + set.add(n); + twoSum += n; // 计算不重复元素之和 + } + } + + return twoSum * 2 - arrSum; + } + + /** + * 使用位运算的 异或 + * 相同的两个数进行异或^结果为0, 0和任何数异或为任何数本身 + *

+ * 如果我们对 0 和二进制位做 XOR 运算,得到的仍然是这个二进制位 + * a ^ 0 = a + * 如果我们对相同的二进制位做 XOR 运算,返回的结果是 0 + * a ^ a = 0 + * XOR 满足交换律和结合律 + * a ^ b ^ a = (a ^ a) ^ b = 0 ^ b = b + *

+ * 链接:https://leetcode-cn.com/problems/single-number/solution/zhi-chu-xian-yi-ci-de-shu-zi-by-leetcode/ + * 来源:力扣(LeetCode) + * + * @return + */ + private static int singleNumberByBit(int[] nums) { + int result = 0; + for (int n : nums) { + result ^= n; // 相同数异或之后变成0, 0和某个数异或得到该数本身 + } + return result; + } +} diff --git a/src/main/java/com/study/search/FindMinAbs.java b/src/main/java/com/study/search/FindMinAbs.java new file mode 100644 index 0000000..5b71061 --- /dev/null +++ b/src/main/java/com/study/search/FindMinAbs.java @@ -0,0 +1,64 @@ +package com.study.search; + +/** + * 找出有序数组中绝对值最小的元素 + */ +public class FindMinAbs { + + public static void main(String[] args) { + // 一个升序整形数组, 找到绝对值最小的输出 + + //int[] arr = {-3,-2,-1,5}; + //int[] arr = {-3, -2, -1, 0, 3, 5}; + //int[] arr = {-1, 1, 3, 5}; + + //int[] arr = {1, 3, 5}; + //int[] arr = {-9, -6, - 2, 0}; + //int[] arr = {-6}; + int[] arr = {}; + System.out.println(min(arr)); + } + + /** + * 利用二分查找进行,使得时间复杂度为O(logN), 低于遍历的O(N) + * + * @param arr + * @return + */ + private static int min(int[] arr) { + int left = 0; + int right = arr.length - 1; + + while (left <= right) { + //int middle = (left + right) / 2; // 这段代码可能越界 + int middle = left + (right - left) >>1; //防止越界 + // 大于0往左找最近的负数 + if (arr[middle] > 0) { + // 判断下一个数是否为负数 + if (middle - 1 >= 0 && arr[middle - 1] < 0) { + // 返回正负交届的绝对值最小的那个数 + if (Math.abs(arr[middle]) > Math.abs(arr[middle - 1])) + return arr[middle - 1]; + else + return arr[middle]; + } else + right = middle - 1; //从左边找, 右边界需要减一,因为有边界这个元素不满足 + // 小于0往右找最近的正数 + } else if (arr[middle] < 0) { + // 判断下一个数是否为正数 (注意边界) + if (middle + 1 < arr.length && arr[middle + 1] > 0) + // 返回正负交届的绝对值最小的那个数 + if (Math.abs(arr[middle]) > Math.abs(arr[middle + 1])) + return arr[middle + 1]; + else + return arr[middle]; + else + left = middle + 1; // 从左边找 左边界需要+1,因为边界这个元素不满足 + } else { + // 如果为0,直接返回 (0是所有数中绝对值最小的) + return arr[middle]; + } + } + return -1; + } +} diff --git a/src/main/java/com/study/search/SearchA2dMatrix.java b/src/main/java/com/study/search/SearchA2dMatrix.java new file mode 100644 index 0000000..cac4dc2 --- /dev/null +++ b/src/main/java/com/study/search/SearchA2dMatrix.java @@ -0,0 +1,38 @@ +package com.study.search; + +/** + * 搜索二维矩阵 + * + * 编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性: + * + * 每行中的整数从左到右按升序排列。 + * 每行的第一个整数大于前一行的最后一个整数。 + * 示例 1: + * + * 输入: + * matrix = [ + * [1, 3, 5, 7], + * [10, 11, 16, 20], + * [23, 30, 34, 50] + * ] + * target = 3 + * 输出: true + * 示例 2: + * + * 输入: + * matrix = [ + * [1, 3, 5, 7], + * [10, 11, 16, 20], + * [23, 30, 34, 50] + * ] + * target = 13 + * 输出: false + * + * 链接:https://leetcode-cn.com/problems/search-a-2d-matrix + */ +public class SearchA2dMatrix { + + public static void main(String[] args) { + + } +} diff --git a/src/main/java/com/study/sort/HeapSort.java b/src/main/java/com/study/sort/HeapSort.java new file mode 100644 index 0000000..2dc7fe9 --- /dev/null +++ b/src/main/java/com/study/sort/HeapSort.java @@ -0,0 +1,15 @@ +package com.study.sort; + +/** + * 堆排序是利用堆这种数据结构而设计的一种排序算法, 堆排序是一种选择排序. + * 它的最好,最坏,平均时间复杂度均为O(NlogN), 它也是不稳定排序. + * + * 堆是具有以下性质的完全二叉树: 每个结点的值都大于或等于其左右孩子节点的值,称为大顶堆; + * 每个结点的值都小于或等于其左右孩子节点的值,称为小顶堆. + */ +public class HeapSort { + + public static void main(String[] args) { + + } +} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy